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"
52 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
54 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
56 class MapEditEventIgnorer
59 MapEditEventIgnorer(bool *flag):
68 ~MapEditEventIgnorer()
81 void * ServerThread::Thread()
85 log_register_thread("ServerThread");
87 DSTACK(__FUNCTION_NAME);
89 BEGIN_DEBUG_EXCEPTION_HANDLER
94 //TimeTaker timer("AsyncRunStep() + Receive()");
97 //TimeTaker timer("AsyncRunStep()");
98 m_server->AsyncRunStep();
101 //infostream<<"Running m_server->Receive()"<<std::endl;
104 catch(con::NoIncomingDataException &e)
107 catch(con::PeerNotFoundException &e)
109 infostream<<"Server: PeerNotFoundException"<<std::endl;
113 END_DEBUG_EXCEPTION_HANDLER(errorstream)
118 void * EmergeThread::Thread()
122 log_register_thread("EmergeThread");
124 DSTACK(__FUNCTION_NAME);
126 BEGIN_DEBUG_EXCEPTION_HANDLER
128 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
131 Get block info from queue, emerge them and send them
134 After queue is empty, exit.
138 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
142 SharedPtr<QueuedBlockEmerge> q(qptr);
148 Do not generate over-limit
150 if(blockpos_over_limit(p))
153 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
155 //TimeTaker timer("block emerge");
158 Try to emerge it from somewhere.
160 If it is only wanted as optional, only loading from disk
165 Check if any peer wants it as non-optional. In that case it
168 Also decrement the emerge queue count in clients.
171 bool only_from_disk = true;
174 core::map<u16, u8>::Iterator i;
175 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
177 //u16 peer_id = i.getNode()->getKey();
180 u8 flags = i.getNode()->getValue();
181 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
182 only_from_disk = false;
187 if(enable_mapgen_debug_info)
188 infostream<<"EmergeThread: p="
189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
190 <<"only_from_disk="<<only_from_disk<<std::endl;
192 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
194 MapBlock *block = NULL;
195 bool got_block = true;
196 core::map<v3s16, MapBlock*> modified_blocks;
199 Try to fetch block from memory or disk.
200 If not found and asked to generate, initialize generator.
203 bool started_generate = false;
204 mapgen::BlockMakeData data;
207 JMutexAutoLock envlock(m_server->m_env_mutex);
209 // Load sector if it isn't loaded
210 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
211 map.loadSectorMeta(p2d);
213 // Attempt to load block
214 block = map.getBlockNoCreateNoEx(p);
215 if(!block || block->isDummy() || !block->isGenerated())
217 if(enable_mapgen_debug_info)
218 infostream<<"EmergeThread: not in memory, "
219 <<"attempting to load from disk"<<std::endl;
221 block = map.loadBlock(p);
224 // If could not load and allowed to generate, start generation
225 // inside this same envlock
226 if(only_from_disk == false &&
227 (block == NULL || block->isGenerated() == false)){
228 if(enable_mapgen_debug_info)
229 infostream<<"EmergeThread: generating"<<std::endl;
230 started_generate = true;
232 map.initBlockMake(&data, p);
237 If generator was initialized, generate now when envlock is free.
242 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
244 TimeTaker t("mapgen::make_block()");
246 mapgen::make_block(&data);
248 if(enable_mapgen_debug_info == false)
249 t.stop(true); // Hide output
253 // Lock environment again to access the map
254 JMutexAutoLock envlock(m_server->m_env_mutex);
256 ScopeProfiler sp(g_profiler, "EmergeThread: after "
257 "mapgen::make_block (envlock)", SPT_AVG);
259 // Blit data back on map, update lighting, add mobs and
260 // whatever this does
261 map.finishBlockMake(&data, modified_blocks);
264 block = map.getBlockNoCreateNoEx(p);
267 Do some post-generate stuff
270 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
271 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
272 scriptapi_environment_on_generated(m_server->m_lua,
275 if(enable_mapgen_debug_info)
276 infostream<<"EmergeThread: ended up with: "
277 <<analyze_block(block)<<std::endl;
280 Ignore map edit events, they will not need to be
281 sent to anybody because the block hasn't been sent
284 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
286 // Activate objects and stuff
287 m_server->m_env->activateBlock(block, 0);
295 Set sent status of modified blocks on clients
298 // NOTE: Server's clients are also behind the connection mutex
299 JMutexAutoLock lock(m_server->m_con_mutex);
302 Add the originally fetched block to the modified list
306 modified_blocks.insert(p, block);
310 Set the modified blocks unsent for all the clients
313 for(core::map<u16, RemoteClient*>::Iterator
314 i = m_server->m_clients.getIterator();
315 i.atEnd() == false; i++)
317 RemoteClient *client = i.getNode()->getValue();
319 if(modified_blocks.size() > 0)
321 // Remove block from sent history
322 client->SetBlocksNotSent(modified_blocks);
328 END_DEBUG_EXCEPTION_HANDLER(errorstream)
330 log_deregister_thread();
335 void RemoteClient::GetNextBlocks(Server *server, float dtime,
336 core::array<PrioritySortedBlockTransfer> &dest)
338 DSTACK(__FUNCTION_NAME);
341 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
344 m_nothing_to_send_pause_timer -= dtime;
345 m_nearest_unsent_reset_timer += dtime;
347 if(m_nothing_to_send_pause_timer >= 0)
352 // Won't send anything if already sending
353 if(m_blocks_sending.size() >= g_settings->getU16
354 ("max_simultaneous_block_sends_per_client"))
356 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
360 //TimeTaker timer("RemoteClient::GetNextBlocks");
362 Player *player = server->m_env->getPlayer(peer_id);
364 assert(player != NULL);
366 v3f playerpos = player->getPosition();
367 v3f playerspeed = player->getSpeed();
368 v3f playerspeeddir(0,0,0);
369 if(playerspeed.getLength() > 1.0*BS)
370 playerspeeddir = playerspeed / playerspeed.getLength();
371 // Predict to next block
372 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
374 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
376 v3s16 center = getNodeBlockPos(center_nodepos);
378 // Camera position and direction
379 v3f camera_pos = player->getEyePosition();
380 v3f camera_dir = v3f(0,0,1);
381 camera_dir.rotateYZBy(player->getPitch());
382 camera_dir.rotateXZBy(player->getYaw());
384 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
385 <<camera_dir.Z<<")"<<std::endl;*/
388 Get the starting value of the block finder radius.
391 if(m_last_center != center)
393 m_nearest_unsent_d = 0;
394 m_last_center = center;
397 /*infostream<<"m_nearest_unsent_reset_timer="
398 <<m_nearest_unsent_reset_timer<<std::endl;*/
400 // Reset periodically to workaround for some bugs or stuff
401 if(m_nearest_unsent_reset_timer > 20.0)
403 m_nearest_unsent_reset_timer = 0;
404 m_nearest_unsent_d = 0;
405 //infostream<<"Resetting m_nearest_unsent_d for "
406 // <<server->getPlayerName(peer_id)<<std::endl;
409 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
410 s16 d_start = m_nearest_unsent_d;
412 //infostream<<"d_start="<<d_start<<std::endl;
414 u16 max_simul_sends_setting = g_settings->getU16
415 ("max_simultaneous_block_sends_per_client");
416 u16 max_simul_sends_usually = max_simul_sends_setting;
419 Check the time from last addNode/removeNode.
421 Decrease send rate if player is building stuff.
423 m_time_from_building += dtime;
424 if(m_time_from_building < g_settings->getFloat(
425 "full_block_send_enable_min_time_from_building"))
427 max_simul_sends_usually
428 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
432 Number of blocks sending + number of blocks selected for sending
434 u32 num_blocks_selected = m_blocks_sending.size();
437 next time d will be continued from the d from which the nearest
438 unsent block was found this time.
440 This is because not necessarily any of the blocks found this
441 time are actually sent.
443 s32 new_nearest_unsent_d = -1;
445 s16 d_max = g_settings->getS16("max_block_send_distance");
446 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
448 // Don't loop very much at a time
449 s16 max_d_increment_at_time = 2;
450 if(d_max > d_start + max_d_increment_at_time)
451 d_max = d_start + max_d_increment_at_time;
452 /*if(d_max_gen > d_start+2)
453 d_max_gen = d_start+2;*/
455 //infostream<<"Starting from "<<d_start<<std::endl;
457 s32 nearest_emerged_d = -1;
458 s32 nearest_emergefull_d = -1;
459 s32 nearest_sent_d = -1;
460 bool queue_is_full = false;
463 for(d = d_start; d <= d_max; d++)
465 /*errorstream<<"checking d="<<d<<" for "
466 <<server->getPlayerName(peer_id)<<std::endl;*/
467 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
470 If m_nearest_unsent_d was changed by the EmergeThread
471 (it can change it to 0 through SetBlockNotSent),
473 Else update m_nearest_unsent_d
475 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
477 d = m_nearest_unsent_d;
478 last_nearest_unsent_d = m_nearest_unsent_d;
482 Get the border/face dot coordinates of a "d-radiused"
485 core::list<v3s16> list;
486 getFacePositions(list, d);
488 core::list<v3s16>::Iterator li;
489 for(li=list.begin(); li!=list.end(); li++)
491 v3s16 p = *li + center;
495 - Don't allow too many simultaneous transfers
496 - EXCEPT when the blocks are very close
498 Also, don't send blocks that are already flying.
501 // Start with the usual maximum
502 u16 max_simul_dynamic = max_simul_sends_usually;
504 // If block is very close, allow full maximum
505 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
506 max_simul_dynamic = max_simul_sends_setting;
508 // Don't select too many blocks for sending
509 if(num_blocks_selected >= max_simul_dynamic)
511 queue_is_full = true;
512 goto queue_full_break;
515 // Don't send blocks that are currently being transferred
516 if(m_blocks_sending.find(p) != NULL)
522 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
527 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
530 // If this is true, inexistent block will be made from scratch
531 bool generate = d <= d_max_gen;
534 /*// Limit the generating area vertically to 2/3
535 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
538 // Limit the send area vertically to 1/2
539 if(abs(p.Y - center.Y) > d_max / 2)
545 If block is far away, don't generate it unless it is
551 // Block center y in nodes
552 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
553 // Don't generate if it's very high or very low
554 if(y < -64 || y > 64)
558 v2s16 p2d_nodes_center(
562 // Get ground height in nodes
563 s16 gh = server->m_env->getServerMap().findGroundLevel(
566 // If differs a lot, don't generate
567 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
569 // Actually, don't even send it
575 //infostream<<"d="<<d<<std::endl;
578 Don't generate or send if not in sight
579 FIXME This only works if the client uses a small enough
580 FOV setting. The default of 72 degrees is fine.
583 float camera_fov = (72.0*PI/180) * 4./3.;
584 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
590 Don't send already sent blocks
593 if(m_blocks_sent.find(p) != NULL)
600 Check if map has this block
602 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
604 bool surely_not_found_on_disk = false;
605 bool block_is_invalid = false;
608 // Reset usage timer, this block will be of use in the future.
609 block->resetUsageTimer();
611 // Block is dummy if data doesn't exist.
612 // It means it has been not found from disk and not generated
615 surely_not_found_on_disk = true;
618 // Block is valid if lighting is up-to-date and data exists
619 if(block->isValid() == false)
621 block_is_invalid = true;
624 /*if(block->isFullyGenerated() == false)
626 block_is_invalid = true;
631 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
632 v2s16 chunkpos = map->sector_to_chunk(p2d);
633 if(map->chunkNonVolatile(chunkpos) == false)
634 block_is_invalid = true;
636 if(block->isGenerated() == false)
637 block_is_invalid = true;
640 If block is not close, don't send it unless it is near
643 Block is near ground level if night-time mesh
644 differs from day-time mesh.
648 if(block->dayNightDiffed() == false)
655 If block has been marked to not exist on disk (dummy)
656 and generating new ones is not wanted, skip block.
658 if(generate == false && surely_not_found_on_disk == true)
665 Add inexistent block to emerge queue.
667 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
669 //TODO: Get value from somewhere
670 // Allow only one block in emerge queue
671 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
672 // Allow two blocks in queue per client
673 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
675 // Make it more responsive when needing to generate stuff
676 if(surely_not_found_on_disk)
678 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
680 //infostream<<"Adding block to emerge queue"<<std::endl;
682 // Add it to the emerge queue and trigger the thread
685 if(generate == false)
686 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
688 server->m_emerge_queue.addBlock(peer_id, p, flags);
689 server->m_emergethread.trigger();
691 if(nearest_emerged_d == -1)
692 nearest_emerged_d = d;
694 if(nearest_emergefull_d == -1)
695 nearest_emergefull_d = d;
702 if(nearest_sent_d == -1)
706 Add block to send queue
709 /*errorstream<<"sending from d="<<d<<" to "
710 <<server->getPlayerName(peer_id)<<std::endl;*/
712 PrioritySortedBlockTransfer q((float)d, p, peer_id);
716 num_blocks_selected += 1;
721 //infostream<<"Stopped at "<<d<<std::endl;
723 // If nothing was found for sending and nothing was queued for
724 // emerging, continue next time browsing from here
725 if(nearest_emerged_d != -1){
726 new_nearest_unsent_d = nearest_emerged_d;
727 } else if(nearest_emergefull_d != -1){
728 new_nearest_unsent_d = nearest_emergefull_d;
730 if(d > g_settings->getS16("max_block_send_distance")){
731 new_nearest_unsent_d = 0;
732 m_nothing_to_send_pause_timer = 2.0;
733 /*infostream<<"GetNextBlocks(): d wrapped around for "
734 <<server->getPlayerName(peer_id)
735 <<"; setting to 0 and pausing"<<std::endl;*/
737 if(nearest_sent_d != -1)
738 new_nearest_unsent_d = nearest_sent_d;
740 new_nearest_unsent_d = d;
744 if(new_nearest_unsent_d != -1)
745 m_nearest_unsent_d = new_nearest_unsent_d;
747 /*timer_result = timer.stop(true);
748 if(timer_result != 0)
749 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
752 void RemoteClient::GotBlock(v3s16 p)
754 if(m_blocks_sending.find(p) != NULL)
755 m_blocks_sending.remove(p);
758 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
759 " m_blocks_sending"<<std::endl;*/
760 m_excess_gotblocks++;
762 m_blocks_sent.insert(p, true);
765 void RemoteClient::SentBlock(v3s16 p)
767 if(m_blocks_sending.find(p) == NULL)
768 m_blocks_sending.insert(p, 0.0);
770 infostream<<"RemoteClient::SentBlock(): Sent block"
771 " already in m_blocks_sending"<<std::endl;
774 void RemoteClient::SetBlockNotSent(v3s16 p)
776 m_nearest_unsent_d = 0;
778 if(m_blocks_sending.find(p) != NULL)
779 m_blocks_sending.remove(p);
780 if(m_blocks_sent.find(p) != NULL)
781 m_blocks_sent.remove(p);
784 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
786 m_nearest_unsent_d = 0;
788 for(core::map<v3s16, MapBlock*>::Iterator
789 i = blocks.getIterator();
790 i.atEnd()==false; i++)
792 v3s16 p = i.getNode()->getKey();
794 if(m_blocks_sending.find(p) != NULL)
795 m_blocks_sending.remove(p);
796 if(m_blocks_sent.find(p) != NULL)
797 m_blocks_sent.remove(p);
805 PlayerInfo::PlayerInfo()
811 void PlayerInfo::PrintLine(std::ostream *s)
814 (*s)<<"\""<<name<<"\" ("
815 <<(position.X/10)<<","<<(position.Y/10)
816 <<","<<(position.Z/10)<<") ";
818 (*s)<<" avg_rtt="<<avg_rtt;
822 u32 PIChecksum(core::list<PlayerInfo> &l)
824 core::list<PlayerInfo>::Iterator i;
827 for(i=l.begin(); i!=l.end(); i++)
829 checksum += a * (i->id+1);
830 checksum ^= 0x435aafcd;
841 std::string mapsavedir,
842 std::string configpath
845 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
846 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
847 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
849 m_toolmgr(createToolDefManager()),
850 m_nodedef(createNodeDefManager()),
851 m_craftdef(createCraftDefManager()),
852 m_craftitemdef(createCraftItemDefManager()),
854 m_emergethread(this),
856 m_time_of_day_send_timer(0),
858 m_mapsavedir(mapsavedir),
859 m_configpath(configpath),
860 m_shutdown_requested(false),
861 m_ignore_map_edit_events(false),
862 m_ignore_map_edit_events_peer_id(0)
864 m_liquid_transform_timer = 0.0;
865 m_print_info_timer = 0.0;
866 m_objectdata_timer = 0.0;
867 m_emergethread_trigger_timer = 0.0;
868 m_savemap_timer = 0.0;
872 m_step_dtime_mutex.Init();
875 JMutexAutoLock envlock(m_env_mutex);
876 JMutexAutoLock conlock(m_con_mutex);
878 // Path to builtin.lua
879 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
881 // Add default global mod search path
882 m_modspaths.push_front(porting::path_data + DIR_DELIM + "mods");
883 // Add world mod search path
884 m_modspaths.push_front(mapsavedir + DIR_DELIM + "worldmods");
885 // Add user mod search path
886 m_modspaths.push_front(porting::path_userdata + DIR_DELIM + "usermods");
888 // Print out mod search paths
889 infostream<<"Mod search paths:"<<std::endl;
890 for(core::list<std::string>::Iterator i = m_modspaths.begin();
891 i != m_modspaths.end(); i++){
892 std::string modspath = *i;
893 infostream<<" "<<modspath<<std::endl;
896 // Initialize scripting
898 infostream<<"Server: Initializing scripting"<<std::endl;
899 m_lua = script_init();
902 scriptapi_export(m_lua, this);
903 // Load and run builtin.lua
904 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
906 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
908 errorstream<<"Server: Failed to load and run "
909 <<builtinpath<<std::endl;
910 throw ModError("Failed to load and run "+builtinpath);
912 // Load and run "mod" scripts
913 core::list<ModSpec> mods = getMods(m_modspaths);
914 for(core::list<ModSpec>::Iterator i = mods.begin();
915 i != mods.end(); i++){
917 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
918 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
919 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
921 errorstream<<"Server: Failed to load and run "
922 <<scriptpath<<std::endl;
923 throw ModError("Failed to load and run "+scriptpath);
927 // Initialize Environment
929 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
932 // Give environment reference to scripting api
933 scriptapi_add_environment(m_lua, m_env);
935 // Register us to receive map edit events
936 m_env->getMap().addEventReceiver(this);
938 // If file exists, load environment metadata
939 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
941 infostream<<"Server: Loading environment metadata"<<std::endl;
942 m_env->loadMeta(m_mapsavedir);
946 infostream<<"Server: Loading players"<<std::endl;
947 m_env->deSerializePlayers(m_mapsavedir);
950 Add some test ActiveBlockModifiers to environment
952 add_legacy_abms(m_env, m_nodedef);
957 infostream<<"Server::~Server()"<<std::endl;
960 Send shutdown message
963 JMutexAutoLock conlock(m_con_mutex);
965 std::wstring line = L"*** Server shutting down";
968 Send the message to clients
970 for(core::map<u16, RemoteClient*>::Iterator
971 i = m_clients.getIterator();
972 i.atEnd() == false; i++)
974 // Get client and check that it is valid
975 RemoteClient *client = i.getNode()->getValue();
976 assert(client->peer_id == i.getNode()->getKey());
977 if(client->serialization_version == SER_FMT_VER_INVALID)
981 SendChatMessage(client->peer_id, line);
983 catch(con::PeerNotFoundException &e)
989 JMutexAutoLock envlock(m_env_mutex);
994 infostream<<"Server: Saving players"<<std::endl;
995 m_env->serializePlayers(m_mapsavedir);
998 Save environment metadata
1000 infostream<<"Server: Saving environment metadata"<<std::endl;
1001 m_env->saveMeta(m_mapsavedir);
1013 JMutexAutoLock clientslock(m_con_mutex);
1015 for(core::map<u16, RemoteClient*>::Iterator
1016 i = m_clients.getIterator();
1017 i.atEnd() == false; i++)
1020 // NOTE: These are removed by env destructor
1022 u16 peer_id = i.getNode()->getKey();
1023 JMutexAutoLock envlock(m_env_mutex);
1024 m_env->removePlayer(peer_id);
1028 delete i.getNode()->getValue();
1032 // Delete Environment
1038 delete m_craftitemdef;
1040 // Deinitialize scripting
1041 infostream<<"Server: Deinitializing scripting"<<std::endl;
1042 script_deinit(m_lua);
1045 void Server::start(unsigned short port)
1047 DSTACK(__FUNCTION_NAME);
1048 // Stop thread if already running
1051 // Initialize connection
1052 m_con.SetTimeoutMs(30);
1056 m_thread.setRun(true);
1059 infostream<<"Server: Started on port "<<port<<std::endl;
1064 DSTACK(__FUNCTION_NAME);
1066 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1068 // Stop threads (set run=false first so both start stopping)
1069 m_thread.setRun(false);
1070 m_emergethread.setRun(false);
1072 m_emergethread.stop();
1074 infostream<<"Server: Threads stopped"<<std::endl;
1077 void Server::step(float dtime)
1079 DSTACK(__FUNCTION_NAME);
1084 JMutexAutoLock lock(m_step_dtime_mutex);
1085 m_step_dtime += dtime;
1089 void Server::AsyncRunStep()
1091 DSTACK(__FUNCTION_NAME);
1093 g_profiler->add("Server::AsyncRunStep (num)", 1);
1097 JMutexAutoLock lock1(m_step_dtime_mutex);
1098 dtime = m_step_dtime;
1102 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1103 // Send blocks to clients
1110 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1112 //infostream<<"Server steps "<<dtime<<std::endl;
1113 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1116 JMutexAutoLock lock1(m_step_dtime_mutex);
1117 m_step_dtime -= dtime;
1124 m_uptime.set(m_uptime.get() + dtime);
1128 // Process connection's timeouts
1129 JMutexAutoLock lock2(m_con_mutex);
1130 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1131 m_con.RunTimeouts(dtime);
1135 // This has to be called so that the client list gets synced
1136 // with the peer list of the connection
1137 handlePeerChanges();
1141 Update m_time_of_day and overall game time
1144 JMutexAutoLock envlock(m_env_mutex);
1146 m_time_counter += dtime;
1147 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1148 u32 units = (u32)(m_time_counter*speed);
1149 m_time_counter -= (f32)units / speed;
1151 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1153 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1156 Send to clients at constant intervals
1159 m_time_of_day_send_timer -= dtime;
1160 if(m_time_of_day_send_timer < 0.0)
1162 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1164 //JMutexAutoLock envlock(m_env_mutex);
1165 JMutexAutoLock conlock(m_con_mutex);
1167 for(core::map<u16, RemoteClient*>::Iterator
1168 i = m_clients.getIterator();
1169 i.atEnd() == false; i++)
1171 RemoteClient *client = i.getNode()->getValue();
1172 //Player *player = m_env->getPlayer(client->peer_id);
1174 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1175 m_env->getTimeOfDay());
1177 m_con.Send(client->peer_id, 0, data, true);
1183 JMutexAutoLock lock(m_env_mutex);
1185 ScopeProfiler sp(g_profiler, "SEnv step");
1186 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1190 const float map_timer_and_unload_dtime = 2.92;
1191 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1193 JMutexAutoLock lock(m_env_mutex);
1194 // Run Map's timers and unload unused data
1195 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1196 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1197 g_settings->getFloat("server_unload_unused_data_timeout"));
1208 JMutexAutoLock lock(m_env_mutex);
1209 JMutexAutoLock lock2(m_con_mutex);
1211 ScopeProfiler sp(g_profiler, "Server: handle players");
1213 //float player_max_speed = BS * 4.0; // Normal speed
1214 float player_max_speed = BS * 20; // Fast speed
1215 float player_max_speed_up = BS * 20;
1217 player_max_speed *= 2.5; // Tolerance
1218 player_max_speed_up *= 2.5;
1220 for(core::map<u16, RemoteClient*>::Iterator
1221 i = m_clients.getIterator();
1222 i.atEnd() == false; i++)
1224 RemoteClient *client = i.getNode()->getValue();
1225 ServerRemotePlayer *player =
1226 static_cast<ServerRemotePlayer*>
1227 (m_env->getPlayer(client->peer_id));
1232 Check player movements
1234 NOTE: Actually the server should handle player physics like the
1235 client does and compare player's position to what is calculated
1236 on our side. This is required when eg. players fly due to an
1239 player->m_last_good_position_age += dtime;
1240 if(player->m_last_good_position_age >= 2.0){
1241 float age = player->m_last_good_position_age;
1242 v3f diff = (player->getPosition() - player->m_last_good_position);
1243 float d_vert = diff.Y;
1245 float d_horiz = diff.getLength();
1246 /*infostream<<player->getName()<<"'s horizontal speed is "
1247 <<(d_horiz/age)<<std::endl;*/
1248 if(d_horiz <= age * player_max_speed &&
1249 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1250 player->m_last_good_position = player->getPosition();
1252 actionstream<<"Player "<<player->getName()
1253 <<" moved too fast; resetting position"
1255 player->setPosition(player->m_last_good_position);
1256 SendMovePlayer(player);
1258 player->m_last_good_position_age = 0;
1262 Handle player HPs (die if hp=0)
1264 HandlePlayerHP(player, 0);
1267 Send player inventories and HPs if necessary
1269 if(player->m_inventory_not_sent){
1270 UpdateCrafting(player->peer_id);
1271 SendInventory(player->peer_id);
1273 if(player->m_hp_not_sent){
1274 SendPlayerHP(player);
1278 Add to environment if is not in respawn screen
1280 if(!player->m_is_in_environment && !player->m_respawn_active){
1281 player->m_removed = false;
1283 m_env->addActiveObject(player);
1288 /* Transform liquids */
1289 m_liquid_transform_timer += dtime;
1290 if(m_liquid_transform_timer >= 1.00)
1292 m_liquid_transform_timer -= 1.00;
1294 JMutexAutoLock lock(m_env_mutex);
1296 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1298 core::map<v3s16, MapBlock*> modified_blocks;
1299 m_env->getMap().transformLiquids(modified_blocks);
1304 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1305 ServerMap &map = ((ServerMap&)m_env->getMap());
1306 map.updateLighting(modified_blocks, lighting_modified_blocks);
1308 // Add blocks modified by lighting to modified_blocks
1309 for(core::map<v3s16, MapBlock*>::Iterator
1310 i = lighting_modified_blocks.getIterator();
1311 i.atEnd() == false; i++)
1313 MapBlock *block = i.getNode()->getValue();
1314 modified_blocks.insert(block->getPos(), block);
1318 Set the modified blocks unsent for all the clients
1321 JMutexAutoLock lock2(m_con_mutex);
1323 for(core::map<u16, RemoteClient*>::Iterator
1324 i = m_clients.getIterator();
1325 i.atEnd() == false; i++)
1327 RemoteClient *client = i.getNode()->getValue();
1329 if(modified_blocks.size() > 0)
1331 // Remove block from sent history
1332 client->SetBlocksNotSent(modified_blocks);
1337 // Periodically print some info
1339 float &counter = m_print_info_timer;
1345 JMutexAutoLock lock2(m_con_mutex);
1347 if(m_clients.size() != 0)
1348 infostream<<"Players:"<<std::endl;
1349 for(core::map<u16, RemoteClient*>::Iterator
1350 i = m_clients.getIterator();
1351 i.atEnd() == false; i++)
1353 //u16 peer_id = i.getNode()->getKey();
1354 RemoteClient *client = i.getNode()->getValue();
1355 Player *player = m_env->getPlayer(client->peer_id);
1358 infostream<<"* "<<player->getName()<<"\t";
1359 client->PrintInfo(infostream);
1364 //if(g_settings->getBool("enable_experimental"))
1368 Check added and deleted active objects
1371 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1372 JMutexAutoLock envlock(m_env_mutex);
1373 JMutexAutoLock conlock(m_con_mutex);
1375 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1377 // Radius inside which objects are active
1378 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1379 radius *= MAP_BLOCKSIZE;
1381 for(core::map<u16, RemoteClient*>::Iterator
1382 i = m_clients.getIterator();
1383 i.atEnd() == false; i++)
1385 RemoteClient *client = i.getNode()->getValue();
1387 // If definitions and textures have not been sent, don't
1388 // send objects either
1389 if(!client->definitions_sent)
1392 Player *player = m_env->getPlayer(client->peer_id);
1395 // This can happen if the client timeouts somehow
1396 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1398 <<" has no associated player"<<std::endl;*/
1401 v3s16 pos = floatToInt(player->getPosition(), BS);
1403 core::map<u16, bool> removed_objects;
1404 core::map<u16, bool> added_objects;
1405 m_env->getRemovedActiveObjects(pos, radius,
1406 client->m_known_objects, removed_objects);
1407 m_env->getAddedActiveObjects(pos, radius,
1408 client->m_known_objects, added_objects);
1410 // Ignore if nothing happened
1411 if(removed_objects.size() == 0 && added_objects.size() == 0)
1413 //infostream<<"active objects: none changed"<<std::endl;
1417 std::string data_buffer;
1421 // Handle removed objects
1422 writeU16((u8*)buf, removed_objects.size());
1423 data_buffer.append(buf, 2);
1424 for(core::map<u16, bool>::Iterator
1425 i = removed_objects.getIterator();
1426 i.atEnd()==false; i++)
1429 u16 id = i.getNode()->getKey();
1430 ServerActiveObject* obj = m_env->getActiveObject(id);
1432 // Add to data buffer for sending
1433 writeU16((u8*)buf, i.getNode()->getKey());
1434 data_buffer.append(buf, 2);
1436 // Remove from known objects
1437 client->m_known_objects.remove(i.getNode()->getKey());
1439 if(obj && obj->m_known_by_count > 0)
1440 obj->m_known_by_count--;
1443 // Handle added objects
1444 writeU16((u8*)buf, added_objects.size());
1445 data_buffer.append(buf, 2);
1446 for(core::map<u16, bool>::Iterator
1447 i = added_objects.getIterator();
1448 i.atEnd()==false; i++)
1451 u16 id = i.getNode()->getKey();
1452 ServerActiveObject* obj = m_env->getActiveObject(id);
1455 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1457 infostream<<"WARNING: "<<__FUNCTION_NAME
1458 <<": NULL object"<<std::endl;
1460 type = obj->getType();
1462 // Add to data buffer for sending
1463 writeU16((u8*)buf, id);
1464 data_buffer.append(buf, 2);
1465 writeU8((u8*)buf, type);
1466 data_buffer.append(buf, 1);
1469 data_buffer.append(serializeLongString(
1470 obj->getClientInitializationData()));
1472 data_buffer.append(serializeLongString(""));
1474 // Add to known objects
1475 client->m_known_objects.insert(i.getNode()->getKey(), false);
1478 obj->m_known_by_count++;
1482 SharedBuffer<u8> reply(2 + data_buffer.size());
1483 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1484 memcpy((char*)&reply[2], data_buffer.c_str(),
1485 data_buffer.size());
1487 m_con.Send(client->peer_id, 0, reply, true);
1489 infostream<<"Server: Sent object remove/add: "
1490 <<removed_objects.size()<<" removed, "
1491 <<added_objects.size()<<" added, "
1492 <<"packet size is "<<reply.getSize()<<std::endl;
1497 Collect a list of all the objects known by the clients
1498 and report it back to the environment.
1501 core::map<u16, bool> all_known_objects;
1503 for(core::map<u16, RemoteClient*>::Iterator
1504 i = m_clients.getIterator();
1505 i.atEnd() == false; i++)
1507 RemoteClient *client = i.getNode()->getValue();
1508 // Go through all known objects of client
1509 for(core::map<u16, bool>::Iterator
1510 i = client->m_known_objects.getIterator();
1511 i.atEnd()==false; i++)
1513 u16 id = i.getNode()->getKey();
1514 all_known_objects[id] = true;
1518 m_env->setKnownActiveObjects(whatever);
1524 Send object messages
1527 JMutexAutoLock envlock(m_env_mutex);
1528 JMutexAutoLock conlock(m_con_mutex);
1530 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1533 // Value = data sent by object
1534 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1536 // Get active object messages from environment
1539 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1543 core::list<ActiveObjectMessage>* message_list = NULL;
1544 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1545 n = buffered_messages.find(aom.id);
1548 message_list = new core::list<ActiveObjectMessage>;
1549 buffered_messages.insert(aom.id, message_list);
1553 message_list = n->getValue();
1555 message_list->push_back(aom);
1558 // Route data to every client
1559 for(core::map<u16, RemoteClient*>::Iterator
1560 i = m_clients.getIterator();
1561 i.atEnd()==false; i++)
1563 RemoteClient *client = i.getNode()->getValue();
1564 std::string reliable_data;
1565 std::string unreliable_data;
1566 // Go through all objects in message buffer
1567 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1568 j = buffered_messages.getIterator();
1569 j.atEnd()==false; j++)
1571 // If object is not known by client, skip it
1572 u16 id = j.getNode()->getKey();
1573 if(client->m_known_objects.find(id) == NULL)
1575 // Get message list of object
1576 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1577 // Go through every message
1578 for(core::list<ActiveObjectMessage>::Iterator
1579 k = list->begin(); k != list->end(); k++)
1581 // Compose the full new data with header
1582 ActiveObjectMessage aom = *k;
1583 std::string new_data;
1586 writeU16((u8*)&buf[0], aom.id);
1587 new_data.append(buf, 2);
1589 new_data += serializeString(aom.datastring);
1590 // Add data to buffer
1592 reliable_data += new_data;
1594 unreliable_data += new_data;
1598 reliable_data and unreliable_data are now ready.
1601 if(reliable_data.size() > 0)
1603 SharedBuffer<u8> reply(2 + reliable_data.size());
1604 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1605 memcpy((char*)&reply[2], reliable_data.c_str(),
1606 reliable_data.size());
1608 m_con.Send(client->peer_id, 0, reply, true);
1610 if(unreliable_data.size() > 0)
1612 SharedBuffer<u8> reply(2 + unreliable_data.size());
1613 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1614 memcpy((char*)&reply[2], unreliable_data.c_str(),
1615 unreliable_data.size());
1616 // Send as unreliable
1617 m_con.Send(client->peer_id, 0, reply, false);
1620 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1622 infostream<<"Server: Size of object message data: "
1623 <<"reliable: "<<reliable_data.size()
1624 <<", unreliable: "<<unreliable_data.size()
1629 // Clear buffered_messages
1630 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1631 i = buffered_messages.getIterator();
1632 i.atEnd()==false; i++)
1634 delete i.getNode()->getValue();
1638 } // enable_experimental
1641 Send queued-for-sending map edit events.
1644 // Don't send too many at a time
1647 // Single change sending is disabled if queue size is not small
1648 bool disable_single_change_sending = false;
1649 if(m_unsent_map_edit_queue.size() >= 4)
1650 disable_single_change_sending = true;
1652 bool got_any_events = false;
1654 // We'll log the amount of each
1657 while(m_unsent_map_edit_queue.size() != 0)
1659 got_any_events = true;
1661 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1663 // Players far away from the change are stored here.
1664 // Instead of sending the changes, MapBlocks are set not sent
1666 core::list<u16> far_players;
1668 if(event->type == MEET_ADDNODE)
1670 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1671 prof.add("MEET_ADDNODE", 1);
1672 if(disable_single_change_sending)
1673 sendAddNode(event->p, event->n, event->already_known_by_peer,
1676 sendAddNode(event->p, event->n, event->already_known_by_peer,
1679 else if(event->type == MEET_REMOVENODE)
1681 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1682 prof.add("MEET_REMOVENODE", 1);
1683 if(disable_single_change_sending)
1684 sendRemoveNode(event->p, event->already_known_by_peer,
1687 sendRemoveNode(event->p, event->already_known_by_peer,
1690 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1692 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1693 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1694 setBlockNotSent(event->p);
1696 else if(event->type == MEET_OTHER)
1698 infostream<<"Server: MEET_OTHER"<<std::endl;
1699 prof.add("MEET_OTHER", 1);
1700 for(core::map<v3s16, bool>::Iterator
1701 i = event->modified_blocks.getIterator();
1702 i.atEnd()==false; i++)
1704 v3s16 p = i.getNode()->getKey();
1710 prof.add("unknown", 1);
1711 infostream<<"WARNING: Server: Unknown MapEditEvent "
1712 <<((u32)event->type)<<std::endl;
1716 Set blocks not sent to far players
1718 if(far_players.size() > 0)
1720 // Convert list format to that wanted by SetBlocksNotSent
1721 core::map<v3s16, MapBlock*> modified_blocks2;
1722 for(core::map<v3s16, bool>::Iterator
1723 i = event->modified_blocks.getIterator();
1724 i.atEnd()==false; i++)
1726 v3s16 p = i.getNode()->getKey();
1727 modified_blocks2.insert(p,
1728 m_env->getMap().getBlockNoCreateNoEx(p));
1730 // Set blocks not sent
1731 for(core::list<u16>::Iterator
1732 i = far_players.begin();
1733 i != far_players.end(); i++)
1736 RemoteClient *client = getClient(peer_id);
1739 client->SetBlocksNotSent(modified_blocks2);
1745 /*// Don't send too many at a time
1747 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1753 infostream<<"Server: MapEditEvents:"<<std::endl;
1754 prof.print(infostream);
1760 Trigger emergethread (it somehow gets to a non-triggered but
1761 bysy state sometimes)
1764 float &counter = m_emergethread_trigger_timer;
1770 m_emergethread.trigger();
1774 // Save map, players and auth stuff
1776 float &counter = m_savemap_timer;
1778 if(counter >= g_settings->getFloat("server_map_save_interval"))
1782 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1785 if(m_authmanager.isModified())
1786 m_authmanager.save();
1789 if(m_banmanager.isModified())
1790 m_banmanager.save();
1793 JMutexAutoLock lock(m_env_mutex);
1795 // Save changed parts of map
1796 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1799 m_env->serializePlayers(m_mapsavedir);
1801 // Save environment metadata
1802 m_env->saveMeta(m_mapsavedir);
1807 void Server::Receive()
1809 DSTACK(__FUNCTION_NAME);
1810 SharedBuffer<u8> data;
1815 JMutexAutoLock conlock(m_con_mutex);
1816 datasize = m_con.Receive(peer_id, data);
1819 // This has to be called so that the client list gets synced
1820 // with the peer list of the connection
1821 handlePeerChanges();
1823 ProcessData(*data, datasize, peer_id);
1825 catch(con::InvalidIncomingDataException &e)
1827 infostream<<"Server::Receive(): "
1828 "InvalidIncomingDataException: what()="
1829 <<e.what()<<std::endl;
1831 catch(con::PeerNotFoundException &e)
1833 //NOTE: This is not needed anymore
1835 // The peer has been disconnected.
1836 // Find the associated player and remove it.
1838 /*JMutexAutoLock envlock(m_env_mutex);
1840 infostream<<"ServerThread: peer_id="<<peer_id
1841 <<" has apparently closed connection. "
1842 <<"Removing player."<<std::endl;
1844 m_env->removePlayer(peer_id);*/
1848 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1850 DSTACK(__FUNCTION_NAME);
1851 // Environment is locked first.
1852 JMutexAutoLock envlock(m_env_mutex);
1853 JMutexAutoLock conlock(m_con_mutex);
1856 Address address = m_con.GetPeerAddress(peer_id);
1858 // drop player if is ip is banned
1859 if(m_banmanager.isIpBanned(address.serializeString())){
1860 SendAccessDenied(m_con, peer_id,
1861 L"Your ip is banned. Banned name was "
1862 +narrow_to_wide(m_banmanager.getBanName(
1863 address.serializeString())));
1864 m_con.DeletePeer(peer_id);
1868 catch(con::PeerNotFoundException &e)
1870 infostream<<"Server::ProcessData(): Cancelling: peer "
1871 <<peer_id<<" not found"<<std::endl;
1875 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1883 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1885 if(command == TOSERVER_INIT)
1887 // [0] u16 TOSERVER_INIT
1888 // [2] u8 SER_FMT_VER_HIGHEST
1889 // [3] u8[20] player_name
1890 // [23] u8[28] password <--- can be sent without this, from old versions
1892 if(datasize < 2+1+PLAYERNAME_SIZE)
1895 infostream<<"Server: Got TOSERVER_INIT from "
1896 <<peer_id<<std::endl;
1898 // First byte after command is maximum supported
1899 // serialization version
1900 u8 client_max = data[2];
1901 u8 our_max = SER_FMT_VER_HIGHEST;
1902 // Use the highest version supported by both
1903 u8 deployed = core::min_(client_max, our_max);
1904 // If it's lower than the lowest supported, give up.
1905 if(deployed < SER_FMT_VER_LOWEST)
1906 deployed = SER_FMT_VER_INVALID;
1908 //peer->serialization_version = deployed;
1909 getClient(peer_id)->pending_serialization_version = deployed;
1911 if(deployed == SER_FMT_VER_INVALID)
1913 infostream<<"Server: Cannot negotiate "
1914 "serialization version with peer "
1915 <<peer_id<<std::endl;
1916 SendAccessDenied(m_con, peer_id, std::wstring(
1917 L"Your client's version is not supported.\n"
1918 L"Server version is ")
1919 + narrow_to_wide(VERSION_STRING) + L"."
1925 Read and check network protocol version
1928 u16 net_proto_version = 0;
1929 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1931 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1934 getClient(peer_id)->net_proto_version = net_proto_version;
1936 if(net_proto_version == 0)
1938 SendAccessDenied(m_con, peer_id, std::wstring(
1939 L"Your client's version is not supported.\n"
1940 L"Server version is ")
1941 + narrow_to_wide(VERSION_STRING) + L"."
1946 if(g_settings->getBool("strict_protocol_version_checking"))
1948 if(net_proto_version != PROTOCOL_VERSION)
1950 SendAccessDenied(m_con, peer_id, std::wstring(
1951 L"Your client's version is not supported.\n"
1952 L"Server version is ")
1953 + narrow_to_wide(VERSION_STRING) + L",\n"
1954 + L"server's PROTOCOL_VERSION is "
1955 + narrow_to_wide(itos(PROTOCOL_VERSION))
1956 + L", client's PROTOCOL_VERSION is "
1957 + narrow_to_wide(itos(net_proto_version))
1968 char playername[PLAYERNAME_SIZE];
1969 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1971 playername[i] = data[3+i];
1973 playername[PLAYERNAME_SIZE-1] = 0;
1975 if(playername[0]=='\0')
1977 infostream<<"Server: Player has empty name"<<std::endl;
1978 SendAccessDenied(m_con, peer_id,
1983 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1985 infostream<<"Server: Player has invalid name"<<std::endl;
1986 SendAccessDenied(m_con, peer_id,
1987 L"Name contains unallowed characters");
1992 char password[PASSWORD_SIZE];
1993 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1995 // old version - assume blank password
2000 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2002 password[i] = data[23+i];
2004 password[PASSWORD_SIZE-1] = 0;
2007 // Add player to auth manager
2008 if(m_authmanager.exists(playername) == false)
2010 std::wstring default_password =
2011 narrow_to_wide(g_settings->get("default_password"));
2012 std::string translated_default_password =
2013 translatePassword(playername, default_password);
2015 // If default_password is empty, allow any initial password
2016 if (default_password.length() == 0)
2017 translated_default_password = password;
2019 infostream<<"Server: adding player "<<playername
2020 <<" to auth manager"<<std::endl;
2021 m_authmanager.add(playername);
2022 m_authmanager.setPassword(playername, translated_default_password);
2023 m_authmanager.setPrivs(playername,
2024 stringToPrivs(g_settings->get("default_privs")));
2025 m_authmanager.save();
2028 std::string checkpwd = m_authmanager.getPassword(playername);
2030 /*infostream<<"Server: Client gave password '"<<password
2031 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2033 if(password != checkpwd)
2035 infostream<<"Server: peer_id="<<peer_id
2036 <<": supplied invalid password for "
2037 <<playername<<std::endl;
2038 SendAccessDenied(m_con, peer_id, L"Invalid password");
2042 // Enforce user limit.
2043 // Don't enforce for users that have some admin right
2044 if(m_clients.size() >= g_settings->getU16("max_users") &&
2045 (m_authmanager.getPrivs(playername)
2046 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2047 playername != g_settings->get("name"))
2049 SendAccessDenied(m_con, peer_id, L"Too many users.");
2054 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2056 // If failed, cancel
2059 infostream<<"Server: peer_id="<<peer_id
2060 <<": failed to emerge player"<<std::endl;
2065 Answer with a TOCLIENT_INIT
2068 SharedBuffer<u8> reply(2+1+6+8);
2069 writeU16(&reply[0], TOCLIENT_INIT);
2070 writeU8(&reply[2], deployed);
2071 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2072 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2075 m_con.Send(peer_id, 0, reply, true);
2079 Send complete position information
2081 SendMovePlayer(player);
2086 if(command == TOSERVER_INIT2)
2088 infostream<<"Server: Got TOSERVER_INIT2 from "
2089 <<peer_id<<std::endl;
2092 getClient(peer_id)->serialization_version
2093 = getClient(peer_id)->pending_serialization_version;
2096 Send some initialization data
2099 // Send tool definitions
2100 SendToolDef(m_con, peer_id, m_toolmgr);
2102 // Send node definitions
2103 SendNodeDef(m_con, peer_id, m_nodedef);
2105 // Send CraftItem definitions
2106 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2109 SendTextures(peer_id);
2111 // Send player info to all players
2112 //SendPlayerInfos();
2114 // Send inventory to player
2115 UpdateCrafting(peer_id);
2116 SendInventory(peer_id);
2118 // Send player items to all players
2121 Player *player = m_env->getPlayer(peer_id);
2124 SendPlayerHP(player);
2128 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2129 m_env->getTimeOfDay());
2130 m_con.Send(peer_id, 0, data, true);
2133 // Now the client should know about everything
2134 getClient(peer_id)->definitions_sent = true;
2136 // Send information about server to player in chat
2137 SendChatMessage(peer_id, getStatusString());
2139 // Send information about joining in chat
2141 std::wstring name = L"unknown";
2142 Player *player = m_env->getPlayer(peer_id);
2144 name = narrow_to_wide(player->getName());
2146 std::wstring message;
2149 message += L" joined game";
2150 BroadcastChatMessage(message);
2153 // Warnings about protocol version can be issued here
2154 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2156 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2160 Check HP, respawn if necessary
2162 HandlePlayerHP(player, 0);
2168 std::ostringstream os(std::ios_base::binary);
2169 for(core::map<u16, RemoteClient*>::Iterator
2170 i = m_clients.getIterator();
2171 i.atEnd() == false; i++)
2173 RemoteClient *client = i.getNode()->getValue();
2174 assert(client->peer_id == i.getNode()->getKey());
2175 if(client->serialization_version == SER_FMT_VER_INVALID)
2178 Player *player = m_env->getPlayer(client->peer_id);
2181 // Get name of player
2182 os<<player->getName()<<" ";
2185 actionstream<<player->getName()<<" joins game. List of players: "
2186 <<os.str()<<std::endl;
2192 if(peer_ser_ver == SER_FMT_VER_INVALID)
2194 infostream<<"Server::ProcessData(): Cancelling: Peer"
2195 " serialization format invalid or not initialized."
2196 " Skipping incoming command="<<command<<std::endl;
2200 Player *player = m_env->getPlayer(peer_id);
2201 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2204 infostream<<"Server::ProcessData(): Cancelling: "
2205 "No player for peer_id="<<peer_id
2209 if(command == TOSERVER_PLAYERPOS)
2211 if(datasize < 2+12+12+4+4)
2215 v3s32 ps = readV3S32(&data[start+2]);
2216 v3s32 ss = readV3S32(&data[start+2+12]);
2217 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2218 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2219 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2220 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2221 pitch = wrapDegrees(pitch);
2222 yaw = wrapDegrees(yaw);
2224 player->setPosition(position);
2225 player->setSpeed(speed);
2226 player->setPitch(pitch);
2227 player->setYaw(yaw);
2229 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2230 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2231 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2233 else if(command == TOSERVER_GOTBLOCKS)
2246 u16 count = data[2];
2247 for(u16 i=0; i<count; i++)
2249 if((s16)datasize < 2+1+(i+1)*6)
2250 throw con::InvalidIncomingDataException
2251 ("GOTBLOCKS length is too short");
2252 v3s16 p = readV3S16(&data[2+1+i*6]);
2253 /*infostream<<"Server: GOTBLOCKS ("
2254 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2255 RemoteClient *client = getClient(peer_id);
2256 client->GotBlock(p);
2259 else if(command == TOSERVER_DELETEDBLOCKS)
2272 u16 count = data[2];
2273 for(u16 i=0; i<count; i++)
2275 if((s16)datasize < 2+1+(i+1)*6)
2276 throw con::InvalidIncomingDataException
2277 ("DELETEDBLOCKS length is too short");
2278 v3s16 p = readV3S16(&data[2+1+i*6]);
2279 /*infostream<<"Server: DELETEDBLOCKS ("
2280 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2281 RemoteClient *client = getClient(peer_id);
2282 client->SetBlockNotSent(p);
2285 else if(command == TOSERVER_CLICK_OBJECT)
2287 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2290 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2292 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2295 else if(command == TOSERVER_GROUND_ACTION)
2297 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2301 else if(command == TOSERVER_RELEASE)
2303 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2306 else if(command == TOSERVER_SIGNTEXT)
2308 infostream<<"Server: SIGNTEXT not supported anymore"
2312 else if(command == TOSERVER_SIGNNODETEXT)
2314 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2322 std::string datastring((char*)&data[2], datasize-2);
2323 std::istringstream is(datastring, std::ios_base::binary);
2326 is.read((char*)buf, 6);
2327 v3s16 p = readV3S16(buf);
2328 is.read((char*)buf, 2);
2329 u16 textlen = readU16(buf);
2331 for(u16 i=0; i<textlen; i++)
2333 is.read((char*)buf, 1);
2334 text += (char)buf[0];
2337 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2341 meta->setText(text);
2343 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2344 <<" at "<<PP(p)<<std::endl;
2346 v3s16 blockpos = getNodeBlockPos(p);
2347 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2350 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2354 setBlockNotSent(blockpos);
2356 else if(command == TOSERVER_INVENTORY_ACTION)
2358 /*// Ignore inventory changes if in creative mode
2359 if(g_settings->getBool("creative_mode") == true)
2361 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2365 // Strip command and create a stream
2366 std::string datastring((char*)&data[2], datasize-2);
2367 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2368 std::istringstream is(datastring, std::ios_base::binary);
2370 InventoryAction *a = InventoryAction::deSerialize(is);
2373 infostream<<"TOSERVER_INVENTORY_ACTION: "
2374 <<"InventoryAction::deSerialize() returned NULL"
2380 c.current_player = player;
2383 Handle restrictions and special cases of the move action
2385 if(a->getType() == IACTION_MOVE
2386 && g_settings->getBool("creative_mode") == false)
2388 InventoryList *rlist = player->inventory.getList("craftresult");
2390 InventoryList *clist = player->inventory.getList("craft");
2392 InventoryList *mlist = player->inventory.getList("main");
2395 IMoveAction *ma = (IMoveAction*)a;
2398 Disable moving items into craftresult from elsewhere
2400 if(ma->to_inv == "current_player"
2401 && ma->to_list == "craftresult"
2402 && (ma->from_inv != "current_player"
2403 || ma->from_list != "craftresult"))
2405 infostream<<"Ignoring IMoveAction from "
2406 <<ma->from_inv<<":"<<ma->from_list
2407 <<" to "<<ma->to_inv<<":"<<ma->to_list
2408 <<" because dst is craftresult"
2409 <<" and src isn't craftresult"<<std::endl;
2415 Handle crafting (source is craftresult, which is preview)
2417 if(ma->from_inv == "current_player"
2418 && ma->from_list == "craftresult"
2419 && player->craftresult_is_preview)
2422 If the craftresult is placed on itself, crafting takes
2423 place and result is moved into main list
2425 if(ma->to_inv == "current_player"
2426 && ma->to_list == "craftresult")
2428 // Except if main list doesn't have free slots
2429 if(mlist->getFreeSlots() == 0){
2430 infostream<<"Cannot craft: Main list doesn't have"
2431 <<" free slots"<<std::endl;
2436 player->craftresult_is_preview = false;
2437 clist->decrementMaterials(1);
2439 InventoryItem *item1 = rlist->changeItem(0, NULL);
2440 mlist->addItem(item1);
2442 srp->m_inventory_not_sent = true;
2448 Disable action if there are no free slots in
2451 If the item is placed on an item that is not of the
2452 same kind, the existing item will be first moved to
2453 craftresult and immediately moved to the free slot.
2456 Inventory *inv_to = getInventory(&c, ma->to_inv);
2458 InventoryList *list_to = inv_to->getList(ma->to_list);
2460 if(list_to->getFreeSlots() == 0){
2461 infostream<<"Cannot craft: Destination doesn't have"
2462 <<" free slots"<<std::endl;
2466 }while(0); // Allow break
2471 player->craftresult_is_preview = false;
2472 clist->decrementMaterials(1);
2474 /* Print out action */
2475 InventoryItem *item = rlist->getItem(0);
2476 std::string itemstring = "NULL";
2478 itemstring = item->getItemString();
2479 actionstream<<player->getName()<<" crafts "
2480 <<itemstring<<std::endl;
2483 a->apply(&c, this, m_env);
2493 // Disallow moving items in elsewhere than player's inventory
2494 // if not allowed to build
2495 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2496 && (ma->from_inv != "current_player"
2497 || ma->to_inv != "current_player"))
2499 infostream<<"Cannot move outside of player's inventory: "
2500 <<"No build privilege"<<std::endl;
2505 // If player is not an admin, check for ownership of src
2506 if(ma->from_inv != "current_player"
2507 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2509 Strfnd fn(ma->from_inv);
2510 std::string id0 = fn.next(":");
2511 if(id0 == "nodemeta")
2514 p.X = stoi(fn.next(","));
2515 p.Y = stoi(fn.next(","));
2516 p.Z = stoi(fn.next(","));
2517 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2518 if(meta->getOwner() != "" &&
2519 meta->getOwner() != player->getName())
2521 infostream<<"Cannot move item: "
2522 "not owner of metadata"
2529 // If player is not an admin, check for ownership of dst
2530 if(ma->to_inv != "current_player"
2531 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2533 Strfnd fn(ma->to_inv);
2534 std::string id0 = fn.next(":");
2535 if(id0 == "nodemeta")
2538 p.X = stoi(fn.next(","));
2539 p.Y = stoi(fn.next(","));
2540 p.Z = stoi(fn.next(","));
2541 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2542 if(meta->getOwner() != "" &&
2543 meta->getOwner() != player->getName())
2545 infostream<<"Cannot move item: "
2546 "not owner of metadata"
2555 Handle restrictions and special cases of the drop action
2557 else if(a->getType() == IACTION_DROP)
2559 IDropAction *da = (IDropAction*)a;
2560 // Disallow dropping items if not allowed to build
2561 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2566 // If player is not an admin, check for ownership
2567 else if (da->from_inv != "current_player"
2568 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2570 Strfnd fn(da->from_inv);
2571 std::string id0 = fn.next(":");
2572 if(id0 == "nodemeta")
2575 p.X = stoi(fn.next(","));
2576 p.Y = stoi(fn.next(","));
2577 p.Z = stoi(fn.next(","));
2578 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2579 if(meta->getOwner() != "" &&
2580 meta->getOwner() != player->getName())
2582 infostream<<"Cannot move item: "
2583 "not owner of metadata"
2593 a->apply(&c, this, m_env);
2597 else if(command == TOSERVER_CHAT_MESSAGE)
2605 std::string datastring((char*)&data[2], datasize-2);
2606 std::istringstream is(datastring, std::ios_base::binary);
2609 is.read((char*)buf, 2);
2610 u16 len = readU16(buf);
2612 std::wstring message;
2613 for(u16 i=0; i<len; i++)
2615 is.read((char*)buf, 2);
2616 message += (wchar_t)readU16(buf);
2619 // Get player name of this client
2620 std::wstring name = narrow_to_wide(player->getName());
2623 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2624 wide_to_narrow(message));
2625 // If script ate the message, don't proceed
2629 // Line to send to players
2631 // Whether to send to the player that sent the line
2632 bool send_to_sender = false;
2633 // Whether to send to other players
2634 bool send_to_others = false;
2636 // Local player gets all privileges regardless of
2637 // what's set on their account.
2638 u64 privs = getPlayerPrivs(player);
2641 if(message[0] == L'/')
2643 size_t strip_size = 1;
2644 if (message[1] == L'#') // support old-style commans
2646 message = message.substr(strip_size);
2648 WStrfnd f1(message);
2649 f1.next(L" "); // Skip over /#whatever
2650 std::wstring paramstring = f1.next(L"");
2652 ServerCommandContext *ctx = new ServerCommandContext(
2653 str_split(message, L' '),
2660 std::wstring reply(processServerCommand(ctx));
2661 send_to_sender = ctx->flags & SEND_TO_SENDER;
2662 send_to_others = ctx->flags & SEND_TO_OTHERS;
2664 if (ctx->flags & SEND_NO_PREFIX)
2667 line += L"Server: " + reply;
2674 if(privs & PRIV_SHOUT)
2680 send_to_others = true;
2684 line += L"Server: You are not allowed to shout";
2685 send_to_sender = true;
2692 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2695 Send the message to clients
2697 for(core::map<u16, RemoteClient*>::Iterator
2698 i = m_clients.getIterator();
2699 i.atEnd() == false; i++)
2701 // Get client and check that it is valid
2702 RemoteClient *client = i.getNode()->getValue();
2703 assert(client->peer_id == i.getNode()->getKey());
2704 if(client->serialization_version == SER_FMT_VER_INVALID)
2708 bool sender_selected = (peer_id == client->peer_id);
2709 if(sender_selected == true && send_to_sender == false)
2711 if(sender_selected == false && send_to_others == false)
2714 SendChatMessage(client->peer_id, line);
2718 else if(command == TOSERVER_DAMAGE)
2720 std::string datastring((char*)&data[2], datasize-2);
2721 std::istringstream is(datastring, std::ios_base::binary);
2722 u8 damage = readU8(is);
2724 if(g_settings->getBool("enable_damage"))
2726 actionstream<<player->getName()<<" damaged by "
2727 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2730 HandlePlayerHP(player, damage);
2734 SendPlayerHP(player);
2737 else if(command == TOSERVER_PASSWORD)
2740 [0] u16 TOSERVER_PASSWORD
2741 [2] u8[28] old password
2742 [30] u8[28] new password
2745 if(datasize != 2+PASSWORD_SIZE*2)
2747 /*char password[PASSWORD_SIZE];
2748 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2749 password[i] = data[2+i];
2750 password[PASSWORD_SIZE-1] = 0;*/
2752 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2760 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2762 char c = data[2+PASSWORD_SIZE+i];
2768 infostream<<"Server: Client requests a password change from "
2769 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2771 std::string playername = player->getName();
2773 if(m_authmanager.exists(playername) == false)
2775 infostream<<"Server: playername not found in authmanager"<<std::endl;
2776 // Wrong old password supplied!!
2777 SendChatMessage(peer_id, L"playername not found in authmanager");
2781 std::string checkpwd = m_authmanager.getPassword(playername);
2783 if(oldpwd != checkpwd)
2785 infostream<<"Server: invalid old password"<<std::endl;
2786 // Wrong old password supplied!!
2787 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2791 actionstream<<player->getName()<<" changes password"<<std::endl;
2793 m_authmanager.setPassword(playername, newpwd);
2795 infostream<<"Server: password change successful for "<<playername
2797 SendChatMessage(peer_id, L"Password change successful");
2799 else if(command == TOSERVER_PLAYERITEM)
2804 u16 item = readU16(&data[2]);
2805 player->wieldItem(item);
2806 SendWieldedItem(player);
2808 else if(command == TOSERVER_RESPAWN)
2813 srp->m_respawn_active = false;
2815 RespawnPlayer(player);
2817 actionstream<<player->getName()<<" respawns at "
2818 <<PP(player->getPosition()/BS)<<std::endl;
2820 // ActiveObject is added to environment in AsyncRunStep after
2821 // the previous addition has been succesfully removed
2823 else if(command == TOSERVER_INTERACT)
2825 std::string datastring((char*)&data[2], datasize-2);
2826 std::istringstream is(datastring, std::ios_base::binary);
2832 [5] u32 length of the next item
2833 [9] serialized PointedThing
2835 0: start digging (from undersurface) or use
2836 1: stop digging (all parameters ignored)
2837 2: digging completed
2838 3: place block or item (to abovesurface)
2841 u8 action = readU8(is);
2842 u16 item_i = readU16(is);
2843 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2844 PointedThing pointed;
2845 pointed.deSerialize(tmp_is);
2847 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2849 v3f player_pos = srp->m_last_good_position;
2851 // Update wielded item
2852 srp->wieldItem(item_i);
2854 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2855 v3s16 p_under = pointed.node_undersurface;
2856 v3s16 p_above = pointed.node_abovesurface;
2858 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2859 ServerActiveObject *pointed_object = NULL;
2860 if(pointed.type == POINTEDTHING_OBJECT)
2862 pointed_object = m_env->getActiveObject(pointed.object_id);
2863 if(pointed_object == NULL)
2865 infostream<<"TOSERVER_INTERACT: "
2866 "pointed object is NULL"<<std::endl;
2873 Check that target is reasonably close
2874 (only when digging or placing things)
2876 if(action == 0 || action == 2 || action == 3)
2878 v3f pointed_pos = player_pos;
2879 if(pointed.type == POINTEDTHING_NODE)
2881 pointed_pos = intToFloat(p_under, BS);
2883 else if(pointed.type == POINTEDTHING_OBJECT)
2885 pointed_pos = pointed_object->getBasePosition();
2888 float d = player_pos.getDistanceFrom(pointed_pos);
2889 float max_d = BS * 10; // Just some large enough value
2891 actionstream<<"Player "<<player->getName()
2892 <<" tried to access "<<pointed.dump()
2894 <<"d="<<d<<", max_d="<<max_d
2895 <<". ignoring."<<std::endl;
2896 // Re-send block to revert change on client-side
2897 RemoteClient *client = getClient(peer_id);
2898 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2899 client->SetBlockNotSent(blockpos);
2906 Make sure the player is allowed to do it
2908 bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
2911 infostream<<"Ignoring interaction from player "<<player->getName()
2912 <<" because privileges are "<<getPlayerPrivs(player)
2914 // NOTE: no return; here, fall through
2918 0: start digging or punch object
2922 if(pointed.type == POINTEDTHING_NODE)
2925 NOTE: This can be used in the future to check if
2926 somebody is cheating, by checking the timing.
2928 bool cannot_punch_node = !build_priv;
2930 MapNode n(CONTENT_IGNORE);
2934 n = m_env->getMap().getNode(p_under);
2936 catch(InvalidPositionException &e)
2938 infostream<<"Server: Not punching: Node not found."
2939 <<" Adding block to emerge queue."
2941 m_emerge_queue.addBlock(peer_id,
2942 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2943 cannot_punch_node = true;
2946 if(cannot_punch_node)
2952 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2954 else if(pointed.type == POINTEDTHING_OBJECT)
2959 // Skip if object has been removed
2960 if(pointed_object->m_removed)
2963 actionstream<<player->getName()<<" punches object "
2964 <<pointed.object_id<<std::endl;
2967 pointed_object->punch(srp);
2975 else if(action == 1)
2980 2: Digging completed
2982 else if(action == 2)
2984 // Only complete digging of nodes
2985 if(pointed.type != POINTEDTHING_NODE)
2988 // Mandatory parameter; actually used for nothing
2989 core::map<v3s16, MapBlock*> modified_blocks;
2991 content_t material = CONTENT_IGNORE;
2992 u8 mineral = MINERAL_NONE;
2994 bool cannot_remove_node = !build_priv;
2996 MapNode n(CONTENT_IGNORE);
2999 n = m_env->getMap().getNode(p_under);
3001 mineral = n.getMineral(m_nodedef);
3002 // Get material at position
3003 material = n.getContent();
3004 // If not yet cancelled
3005 if(cannot_remove_node == false)
3007 // If it's not diggable, do nothing
3008 if(m_nodedef->get(material).diggable == false)
3010 infostream<<"Server: Not finishing digging: "
3011 <<"Node not diggable"
3013 cannot_remove_node = true;
3016 // If not yet cancelled
3017 if(cannot_remove_node == false)
3019 // Get node metadata
3020 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3021 if(meta && meta->nodeRemovalDisabled() == true)
3023 infostream<<"Server: Not finishing digging: "
3024 <<"Node metadata disables removal"
3026 cannot_remove_node = true;
3030 catch(InvalidPositionException &e)
3032 infostream<<"Server: Not finishing digging: Node not found."
3033 <<" Adding block to emerge queue."
3035 m_emerge_queue.addBlock(peer_id,
3036 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3037 cannot_remove_node = true;
3041 If node can't be removed, set block to be re-sent to
3044 if(cannot_remove_node)
3046 infostream<<"Server: Not finishing digging."<<std::endl;
3048 // Client probably has wrong data.
3049 // Set block not sent, so that client will get
3051 infostream<<"Client "<<peer_id<<" tried to dig "
3052 <<"node; but node cannot be removed."
3053 <<" setting MapBlock not sent."<<std::endl;
3054 RemoteClient *client = getClient(peer_id);
3055 v3s16 blockpos = getNodeBlockPos(p_under);
3056 client->SetBlockNotSent(blockpos);
3061 actionstream<<player->getName()<<" digs "<<PP(p_under)
3062 <<", gets material "<<(int)material<<", mineral "
3063 <<(int)mineral<<std::endl;
3066 Send the removal to all close-by players.
3067 - If other player is close, send REMOVENODE
3068 - Otherwise set blocks not sent
3070 core::list<u16> far_players;
3071 sendRemoveNode(p_under, peer_id, &far_players, 30);
3074 Update and send inventory
3077 if(g_settings->getBool("creative_mode") == false)
3082 InventoryList *mlist = player->inventory.getList("main");
3085 InventoryItem *item = mlist->getItem(item_i);
3086 if(item && (std::string)item->getName() == "ToolItem")
3088 ToolItem *titem = (ToolItem*)item;
3089 std::string toolname = titem->getToolName();
3091 // Get digging properties for material and tool
3092 ToolDiggingProperties tp =
3093 m_toolmgr->getDiggingProperties(toolname);
3094 DiggingProperties prop =
3095 getDiggingProperties(material, &tp, m_nodedef);
3097 if(prop.diggable == false)
3099 infostream<<"Server: WARNING: Player digged"
3100 <<" with impossible material + tool"
3101 <<" combination"<<std::endl;
3104 bool weared_out = titem->addWear(prop.wear);
3108 mlist->deleteItem(item_i);
3111 srp->m_inventory_not_sent = true;
3116 Add dug item to inventory
3119 InventoryItem *item = NULL;
3121 if(mineral != MINERAL_NONE)
3122 item = getDiggedMineralItem(mineral, this);
3127 const std::string &dug_s = m_nodedef->get(material).dug_item;
3130 std::istringstream is(dug_s, std::ios::binary);
3131 item = InventoryItem::deSerialize(is, this);
3137 // Add a item to inventory
3138 player->inventory.addItem("main", item);
3139 srp->m_inventory_not_sent = true;
3144 if(mineral != MINERAL_NONE)
3145 item = getDiggedMineralItem(mineral, this);
3150 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3151 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3152 if(extra_dug_s != "" && extra_rarity != 0
3153 && myrand() % extra_rarity == 0)
3155 std::istringstream is(extra_dug_s, std::ios::binary);
3156 item = InventoryItem::deSerialize(is, this);
3162 // Add a item to inventory
3163 player->inventory.addItem("main", item);
3164 srp->m_inventory_not_sent = true;
3170 (this takes some time so it is done after the quick stuff)
3173 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3175 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3178 Set blocks not sent to far players
3180 for(core::list<u16>::Iterator
3181 i = far_players.begin();
3182 i != far_players.end(); i++)
3185 RemoteClient *client = getClient(peer_id);
3188 client->SetBlocksNotSent(modified_blocks);
3194 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3198 3: place block or right-click object
3200 else if(action == 3)
3202 if(pointed.type == POINTEDTHING_NODE)
3204 InventoryList *ilist = player->inventory.getList("main");
3209 InventoryItem *item = ilist->getItem(item_i);
3211 // If there is no item, it is not possible to add it anywhere
3216 Handle material items
3218 if(std::string("MaterialItem") == item->getName())
3220 bool cannot_place_node = !build_priv;
3223 // Don't add a node if this is not a free space
3224 MapNode n2 = m_env->getMap().getNode(p_above);
3225 if(m_nodedef->get(n2).buildable_to == false)
3227 infostream<<"Client "<<peer_id<<" tried to place"
3228 <<" node in invalid position."<<std::endl;
3229 cannot_place_node = true;
3232 catch(InvalidPositionException &e)
3234 infostream<<"Server: Ignoring ADDNODE: Node not found"
3235 <<" Adding block to emerge queue."
3237 m_emerge_queue.addBlock(peer_id,
3238 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3239 cannot_place_node = true;
3242 if(cannot_place_node)
3244 // Client probably has wrong data.
3245 // Set block not sent, so that client will get
3247 RemoteClient *client = getClient(peer_id);
3248 v3s16 blockpos = getNodeBlockPos(p_above);
3249 client->SetBlockNotSent(blockpos);
3253 // Reset build time counter
3254 getClient(peer_id)->m_time_from_building = 0.0;
3257 MaterialItem *mitem = (MaterialItem*)item;
3259 n.setContent(mitem->getMaterial());
3261 actionstream<<player->getName()<<" places material "
3262 <<(int)mitem->getMaterial()
3263 <<" at "<<PP(p_under)<<std::endl;
3265 // Calculate direction for wall mounted stuff
3266 if(m_nodedef->get(n).wall_mounted)
3267 n.param2 = packDir(p_under - p_above);
3269 // Calculate the direction for furnaces and chests and stuff
3270 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3272 v3f playerpos = player->getPosition();
3273 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3274 blockpos = blockpos.normalize();
3276 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3290 Send to all close-by players
3292 core::list<u16> far_players;
3293 sendAddNode(p_above, n, 0, &far_players, 30);
3298 InventoryList *ilist = player->inventory.getList("main");
3299 if(g_settings->getBool("creative_mode") == false && ilist)
3301 // Remove from inventory and send inventory
3302 if(mitem->getCount() <= 1)
3303 ilist->deleteItem(item_i);
3306 srp->m_inventory_not_sent = true;
3312 This takes some time so it is done after the quick stuff
3314 core::map<v3s16, MapBlock*> modified_blocks;
3316 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3318 std::string p_name = std::string(player->getName());
3319 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3322 Set blocks not sent to far players
3324 for(core::list<u16>::Iterator
3325 i = far_players.begin();
3326 i != far_players.end(); i++)
3329 RemoteClient *client = getClient(peer_id);
3332 client->SetBlocksNotSent(modified_blocks);
3338 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3341 Calculate special events
3344 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3347 for(s16 z=-1; z<=1; z++)
3348 for(s16 y=-1; y<=1; y++)
3349 for(s16 x=-1; x<=1; x++)
3356 Place other item (not a block)
3362 infostream<<"Not allowing player to place item: "
3363 "no build privileges"<<std::endl;
3367 // Calculate a position for it
3368 v3f pos = player_pos;
3369 if(pointed.type == POINTEDTHING_NOTHING)
3371 infostream<<"Not allowing player to place item: "
3372 "pointing to nothing"<<std::endl;
3375 else if(pointed.type == POINTEDTHING_NODE)
3377 pos = intToFloat(p_above, BS);
3379 else if(pointed.type == POINTEDTHING_OBJECT)
3381 pos = pointed_object->getBasePosition();
3384 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3385 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3389 //pos.Y -= BS*0.25; // let it drop a bit
3392 Check that the block is loaded so that the item
3393 can properly be added to the static list too
3395 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3396 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3399 infostream<<"Error while placing item: "
3400 "block not found"<<std::endl;
3404 actionstream<<player->getName()<<" places "<<item->getName()
3405 <<" at "<<PP(pos)<<std::endl;
3410 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3411 if(remove && g_settings->getBool("creative_mode") == false)
3413 InventoryList *ilist = player->inventory.getList("main");
3415 // Remove from inventory and send inventory
3416 ilist->deleteItem(item_i);
3417 srp->m_inventory_not_sent = true;
3422 else if(pointed.type == POINTEDTHING_OBJECT)
3424 // Right click object
3429 // Skip if object has been removed
3430 if(pointed_object->m_removed)
3433 actionstream<<player->getName()<<" right-clicks object "
3434 <<pointed.object_id<<std::endl;
3437 pointed_object->rightClick(srp);
3445 else if(action == 4)
3447 InventoryList *ilist = player->inventory.getList("main");
3452 InventoryItem *item = ilist->getItem(item_i);
3454 // If there is no item, it is not possible to add it anywhere
3458 // Requires build privs
3461 infostream<<"Not allowing player to use item: "
3462 "no build privileges"<<std::endl;
3466 actionstream<<player->getName()<<" uses "<<item->getName()
3467 <<", pointing at "<<pointed.dump()<<std::endl;
3469 bool remove = item->use(m_env, srp, pointed);
3471 if(remove && g_settings->getBool("creative_mode") == false)
3473 InventoryList *ilist = player->inventory.getList("main");
3475 // Remove from inventory and send inventory
3476 ilist->deleteItem(item_i);
3477 srp->m_inventory_not_sent = true;
3484 Catch invalid actions
3488 infostream<<"WARNING: Server: Invalid action "
3489 <<action<<std::endl;
3492 // Complete add_to_inventory_later
3493 srp->completeAddToInventoryLater(item_i);
3497 infostream<<"Server::ProcessData(): Ignoring "
3498 "unknown command "<<command<<std::endl;
3502 catch(SendFailedException &e)
3504 errorstream<<"Server::ProcessData(): SendFailedException: "
3510 void Server::onMapEditEvent(MapEditEvent *event)
3512 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3513 if(m_ignore_map_edit_events)
3515 MapEditEvent *e = event->clone();
3516 m_unsent_map_edit_queue.push_back(e);
3519 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3521 if(id == "current_player")
3523 assert(c->current_player);
3524 return &(c->current_player->inventory);
3528 std::string id0 = fn.next(":");
3530 if(id0 == "nodemeta")
3533 p.X = stoi(fn.next(","));
3534 p.Y = stoi(fn.next(","));
3535 p.Z = stoi(fn.next(","));
3536 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3538 return meta->getInventory();
3539 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3540 <<"no metadata found"<<std::endl;
3544 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3547 void Server::inventoryModified(InventoryContext *c, std::string id)
3549 if(id == "current_player")
3551 assert(c->current_player);
3552 ServerRemotePlayer *srp =
3553 static_cast<ServerRemotePlayer*>(c->current_player);
3554 srp->m_inventory_not_sent = true;
3559 std::string id0 = fn.next(":");
3561 if(id0 == "nodemeta")
3564 p.X = stoi(fn.next(","));
3565 p.Y = stoi(fn.next(","));
3566 p.Z = stoi(fn.next(","));
3567 v3s16 blockpos = getNodeBlockPos(p);
3569 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3571 meta->inventoryModified();
3573 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3575 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3577 setBlockNotSent(blockpos);
3582 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3585 core::list<PlayerInfo> Server::getPlayerInfo()
3587 DSTACK(__FUNCTION_NAME);
3588 JMutexAutoLock envlock(m_env_mutex);
3589 JMutexAutoLock conlock(m_con_mutex);
3591 core::list<PlayerInfo> list;
3593 core::list<Player*> players = m_env->getPlayers();
3595 core::list<Player*>::Iterator i;
3596 for(i = players.begin();
3597 i != players.end(); i++)
3601 Player *player = *i;
3604 // Copy info from connection to info struct
3605 info.id = player->peer_id;
3606 info.address = m_con.GetPeerAddress(player->peer_id);
3607 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3609 catch(con::PeerNotFoundException &e)
3611 // Set dummy peer info
3613 info.address = Address(0,0,0,0,0);
3617 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3618 info.position = player->getPosition();
3620 list.push_back(info);
3627 void Server::peerAdded(con::Peer *peer)
3629 DSTACK(__FUNCTION_NAME);
3630 infostream<<"Server::peerAdded(): peer->id="
3631 <<peer->id<<std::endl;
3634 c.type = PEER_ADDED;
3635 c.peer_id = peer->id;
3637 m_peer_change_queue.push_back(c);
3640 void Server::deletingPeer(con::Peer *peer, bool timeout)
3642 DSTACK(__FUNCTION_NAME);
3643 infostream<<"Server::deletingPeer(): peer->id="
3644 <<peer->id<<", timeout="<<timeout<<std::endl;
3647 c.type = PEER_REMOVED;
3648 c.peer_id = peer->id;
3649 c.timeout = timeout;
3650 m_peer_change_queue.push_back(c);
3657 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3659 DSTACK(__FUNCTION_NAME);
3660 std::ostringstream os(std::ios_base::binary);
3662 writeU16(os, TOCLIENT_HP);
3666 std::string s = os.str();
3667 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3669 con.Send(peer_id, 0, data, true);
3672 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3673 const std::wstring &reason)
3675 DSTACK(__FUNCTION_NAME);
3676 std::ostringstream os(std::ios_base::binary);
3678 writeU16(os, TOCLIENT_ACCESS_DENIED);
3679 os<<serializeWideString(reason);
3682 std::string s = os.str();
3683 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3685 con.Send(peer_id, 0, data, true);
3688 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3689 bool set_camera_point_target, v3f camera_point_target)
3691 DSTACK(__FUNCTION_NAME);
3692 std::ostringstream os(std::ios_base::binary);
3694 writeU16(os, TOCLIENT_DEATHSCREEN);
3695 writeU8(os, set_camera_point_target);
3696 writeV3F1000(os, camera_point_target);
3699 std::string s = os.str();
3700 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3702 con.Send(peer_id, 0, data, true);
3705 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3706 IToolDefManager *tooldef)
3708 DSTACK(__FUNCTION_NAME);
3709 std::ostringstream os(std::ios_base::binary);
3713 u32 length of the next item
3714 serialized ToolDefManager
3716 writeU16(os, TOCLIENT_TOOLDEF);
3717 std::ostringstream tmp_os(std::ios::binary);
3718 tooldef->serialize(tmp_os);
3719 os<<serializeLongString(tmp_os.str());
3722 std::string s = os.str();
3723 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3724 <<s.size()<<std::endl;
3725 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3727 con.Send(peer_id, 0, data, true);
3730 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3731 INodeDefManager *nodedef)
3733 DSTACK(__FUNCTION_NAME);
3734 std::ostringstream os(std::ios_base::binary);
3738 u32 length of the next item
3739 serialized NodeDefManager
3741 writeU16(os, TOCLIENT_NODEDEF);
3742 std::ostringstream tmp_os(std::ios::binary);
3743 nodedef->serialize(tmp_os);
3744 os<<serializeLongString(tmp_os.str());
3747 std::string s = os.str();
3748 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3749 <<s.size()<<std::endl;
3750 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3752 con.Send(peer_id, 0, data, true);
3755 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3756 ICraftItemDefManager *craftitemdef)
3758 DSTACK(__FUNCTION_NAME);
3759 std::ostringstream os(std::ios_base::binary);
3763 u32 length of the next item
3764 serialized CraftItemDefManager
3766 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3767 std::ostringstream tmp_os(std::ios::binary);
3768 craftitemdef->serialize(tmp_os);
3769 os<<serializeLongString(tmp_os.str());
3772 std::string s = os.str();
3773 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3774 <<s.size()<<std::endl;
3775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3777 con.Send(peer_id, 0, data, true);
3781 Non-static send methods
3784 void Server::SendInventory(u16 peer_id)
3786 DSTACK(__FUNCTION_NAME);
3788 ServerRemotePlayer* player =
3789 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3792 player->m_inventory_not_sent = false;
3798 std::ostringstream os;
3799 //os.imbue(std::locale("C"));
3801 player->inventory.serialize(os);
3803 std::string s = os.str();
3805 SharedBuffer<u8> data(s.size()+2);
3806 writeU16(&data[0], TOCLIENT_INVENTORY);
3807 memcpy(&data[2], s.c_str(), s.size());
3810 m_con.Send(peer_id, 0, data, true);
3813 std::string getWieldedItemString(const Player *player)
3815 const InventoryItem *item = player->getWieldItem();
3817 return std::string("");
3818 std::ostringstream os(std::ios_base::binary);
3819 item->serialize(os);
3823 void Server::SendWieldedItem(const Player* player)
3825 DSTACK(__FUNCTION_NAME);
3829 std::ostringstream os(std::ios_base::binary);
3831 writeU16(os, TOCLIENT_PLAYERITEM);
3833 writeU16(os, player->peer_id);
3834 os<<serializeString(getWieldedItemString(player));
3837 std::string s = os.str();
3838 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3840 m_con.SendToAll(0, data, true);
3843 void Server::SendPlayerItems()
3845 DSTACK(__FUNCTION_NAME);
3847 std::ostringstream os(std::ios_base::binary);
3848 core::list<Player *> players = m_env->getPlayers(true);
3850 writeU16(os, TOCLIENT_PLAYERITEM);
3851 writeU16(os, players.size());
3852 core::list<Player *>::Iterator i;
3853 for(i = players.begin(); i != players.end(); ++i)
3856 writeU16(os, p->peer_id);
3857 os<<serializeString(getWieldedItemString(p));
3861 std::string s = os.str();
3862 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3864 m_con.SendToAll(0, data, true);
3867 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3869 DSTACK(__FUNCTION_NAME);
3871 std::ostringstream os(std::ios_base::binary);
3875 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3876 os.write((char*)buf, 2);
3879 writeU16(buf, message.size());
3880 os.write((char*)buf, 2);
3883 for(u32 i=0; i<message.size(); i++)
3887 os.write((char*)buf, 2);
3891 std::string s = os.str();
3892 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3894 m_con.Send(peer_id, 0, data, true);
3897 void Server::BroadcastChatMessage(const std::wstring &message)
3899 for(core::map<u16, RemoteClient*>::Iterator
3900 i = m_clients.getIterator();
3901 i.atEnd() == false; i++)
3903 // Get client and check that it is valid
3904 RemoteClient *client = i.getNode()->getValue();
3905 assert(client->peer_id == i.getNode()->getKey());
3906 if(client->serialization_version == SER_FMT_VER_INVALID)
3909 SendChatMessage(client->peer_id, message);
3913 void Server::SendPlayerHP(Player *player)
3915 SendHP(m_con, player->peer_id, player->hp);
3916 static_cast<ServerRemotePlayer*>(player)->m_hp_not_sent = false;
3919 void Server::SendMovePlayer(Player *player)
3921 DSTACK(__FUNCTION_NAME);
3922 std::ostringstream os(std::ios_base::binary);
3924 writeU16(os, TOCLIENT_MOVE_PLAYER);
3925 writeV3F1000(os, player->getPosition());
3926 writeF1000(os, player->getPitch());
3927 writeF1000(os, player->getYaw());
3930 v3f pos = player->getPosition();
3931 f32 pitch = player->getPitch();
3932 f32 yaw = player->getYaw();
3933 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3934 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3941 std::string s = os.str();
3942 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3944 m_con.Send(player->peer_id, 0, data, true);
3947 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3948 core::list<u16> *far_players, float far_d_nodes)
3950 float maxd = far_d_nodes*BS;
3951 v3f p_f = intToFloat(p, BS);
3955 SharedBuffer<u8> reply(replysize);
3956 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3957 writeS16(&reply[2], p.X);
3958 writeS16(&reply[4], p.Y);
3959 writeS16(&reply[6], p.Z);
3961 for(core::map<u16, RemoteClient*>::Iterator
3962 i = m_clients.getIterator();
3963 i.atEnd() == false; i++)
3965 // Get client and check that it is valid
3966 RemoteClient *client = i.getNode()->getValue();
3967 assert(client->peer_id == i.getNode()->getKey());
3968 if(client->serialization_version == SER_FMT_VER_INVALID)
3971 // Don't send if it's the same one
3972 if(client->peer_id == ignore_id)
3978 Player *player = m_env->getPlayer(client->peer_id);
3981 // If player is far away, only set modified blocks not sent
3982 v3f player_pos = player->getPosition();
3983 if(player_pos.getDistanceFrom(p_f) > maxd)
3985 far_players->push_back(client->peer_id);
3992 m_con.Send(client->peer_id, 0, reply, true);
3996 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3997 core::list<u16> *far_players, float far_d_nodes)
3999 float maxd = far_d_nodes*BS;
4000 v3f p_f = intToFloat(p, BS);
4002 for(core::map<u16, RemoteClient*>::Iterator
4003 i = m_clients.getIterator();
4004 i.atEnd() == false; i++)
4006 // Get client and check that it is valid
4007 RemoteClient *client = i.getNode()->getValue();
4008 assert(client->peer_id == i.getNode()->getKey());
4009 if(client->serialization_version == SER_FMT_VER_INVALID)
4012 // Don't send if it's the same one
4013 if(client->peer_id == ignore_id)
4019 Player *player = m_env->getPlayer(client->peer_id);
4022 // If player is far away, only set modified blocks not sent
4023 v3f player_pos = player->getPosition();
4024 if(player_pos.getDistanceFrom(p_f) > maxd)
4026 far_players->push_back(client->peer_id);
4033 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4034 SharedBuffer<u8> reply(replysize);
4035 writeU16(&reply[0], TOCLIENT_ADDNODE);
4036 writeS16(&reply[2], p.X);
4037 writeS16(&reply[4], p.Y);
4038 writeS16(&reply[6], p.Z);
4039 n.serialize(&reply[8], client->serialization_version);
4042 m_con.Send(client->peer_id, 0, reply, true);
4046 void Server::setBlockNotSent(v3s16 p)
4048 for(core::map<u16, RemoteClient*>::Iterator
4049 i = m_clients.getIterator();
4050 i.atEnd()==false; i++)
4052 RemoteClient *client = i.getNode()->getValue();
4053 client->SetBlockNotSent(p);
4057 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4059 DSTACK(__FUNCTION_NAME);
4061 v3s16 p = block->getPos();
4065 bool completely_air = true;
4066 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4067 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4068 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4070 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4072 completely_air = false;
4073 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4078 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4080 infostream<<"[completely air] ";
4081 infostream<<std::endl;
4085 Create a packet with the block in the right format
4088 std::ostringstream os(std::ios_base::binary);
4089 block->serialize(os, ver);
4090 std::string s = os.str();
4091 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4093 u32 replysize = 8 + blockdata.getSize();
4094 SharedBuffer<u8> reply(replysize);
4095 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4096 writeS16(&reply[2], p.X);
4097 writeS16(&reply[4], p.Y);
4098 writeS16(&reply[6], p.Z);
4099 memcpy(&reply[8], *blockdata, blockdata.getSize());
4101 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4102 <<": \tpacket size: "<<replysize<<std::endl;*/
4107 m_con.Send(peer_id, 1, reply, true);
4110 void Server::SendBlocks(float dtime)
4112 DSTACK(__FUNCTION_NAME);
4114 JMutexAutoLock envlock(m_env_mutex);
4115 JMutexAutoLock conlock(m_con_mutex);
4117 //TimeTaker timer("Server::SendBlocks");
4119 core::array<PrioritySortedBlockTransfer> queue;
4121 s32 total_sending = 0;
4124 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4126 for(core::map<u16, RemoteClient*>::Iterator
4127 i = m_clients.getIterator();
4128 i.atEnd() == false; i++)
4130 RemoteClient *client = i.getNode()->getValue();
4131 assert(client->peer_id == i.getNode()->getKey());
4133 // If definitions and textures have not been sent, don't
4134 // send MapBlocks either
4135 if(!client->definitions_sent)
4138 total_sending += client->SendingCount();
4140 if(client->serialization_version == SER_FMT_VER_INVALID)
4143 client->GetNextBlocks(this, dtime, queue);
4148 // Lowest priority number comes first.
4149 // Lowest is most important.
4152 for(u32 i=0; i<queue.size(); i++)
4154 //TODO: Calculate limit dynamically
4155 if(total_sending >= g_settings->getS32
4156 ("max_simultaneous_block_sends_server_total"))
4159 PrioritySortedBlockTransfer q = queue[i];
4161 MapBlock *block = NULL;
4164 block = m_env->getMap().getBlockNoCreate(q.pos);
4166 catch(InvalidPositionException &e)
4171 RemoteClient *client = getClient(q.peer_id);
4173 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4175 client->SentBlock(q.pos);
4181 struct SendableTexture
4187 SendableTexture(const std::string &name_="", const std::string path_="",
4188 const std::string &data_=""):
4195 void Server::SendTextures(u16 peer_id)
4197 DSTACK(__FUNCTION_NAME);
4199 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4203 // Put 5kB in one bunch (this is not accurate)
4204 u32 bytes_per_bunch = 5000;
4206 core::array< core::list<SendableTexture> > texture_bunches;
4207 texture_bunches.push_back(core::list<SendableTexture>());
4209 u32 texture_size_bunch_total = 0;
4210 core::list<ModSpec> mods = getMods(m_modspaths);
4211 for(core::list<ModSpec>::Iterator i = mods.begin();
4212 i != mods.end(); i++){
4214 std::string texturepath = mod.path + DIR_DELIM + "textures";
4215 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4216 for(u32 j=0; j<dirlist.size(); j++){
4217 if(dirlist[j].dir) // Ignode dirs
4219 std::string tname = dirlist[j].name;
4220 std::string tpath = texturepath + DIR_DELIM + tname;
4222 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4223 if(fis.good() == false){
4224 errorstream<<"Server::SendTextures(): Could not open \""
4225 <<tname<<"\" for reading"<<std::endl;
4228 std::ostringstream tmp_os(std::ios_base::binary);
4232 fis.read(buf, 1024);
4233 std::streamsize len = fis.gcount();
4234 tmp_os.write(buf, len);
4235 texture_size_bunch_total += len;
4244 errorstream<<"Server::SendTextures(): Failed to read \""
4245 <<tname<<"\""<<std::endl;
4248 /*infostream<<"Server::SendTextures(): Loaded \""
4249 <<tname<<"\""<<std::endl;*/
4251 texture_bunches[texture_bunches.size()-1].push_back(
4252 SendableTexture(tname, tpath, tmp_os.str()));
4254 // Start next bunch if got enough data
4255 if(texture_size_bunch_total >= bytes_per_bunch){
4256 texture_bunches.push_back(core::list<SendableTexture>());
4257 texture_size_bunch_total = 0;
4262 /* Create and send packets */
4264 u32 num_bunches = texture_bunches.size();
4265 for(u32 i=0; i<num_bunches; i++)
4269 u16 total number of texture bunches
4270 u16 index of this bunch
4271 u32 number of textures in this bunch
4279 std::ostringstream os(std::ios_base::binary);
4281 writeU16(os, TOCLIENT_TEXTURES);
4282 writeU16(os, num_bunches);
4284 writeU32(os, texture_bunches[i].size());
4286 for(core::list<SendableTexture>::Iterator
4287 j = texture_bunches[i].begin();
4288 j != texture_bunches[i].end(); j++){
4289 os<<serializeString(j->name);
4290 os<<serializeLongString(j->data);
4294 std::string s = os.str();
4295 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4296 <<" textures="<<texture_bunches[i].size()
4297 <<" size=" <<s.size()<<std::endl;
4298 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4300 m_con.Send(peer_id, 0, data, true);
4308 void Server::HandlePlayerHP(Player *player, s16 damage)
4310 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4312 if(srp->m_respawn_active)
4318 if(player->hp > damage)
4320 player->hp -= damage;
4321 SendPlayerHP(player);
4325 infostream<<"Server::HandlePlayerHP(): Player "
4326 <<player->getName()<<" dies"<<std::endl;
4330 //TODO: Throw items around
4332 // Handle players that are not connected
4333 if(player->peer_id == PEER_ID_INEXISTENT){
4334 RespawnPlayer(player);
4338 SendPlayerHP(player);
4340 RemoteClient *client = getClient(player->peer_id);
4341 if(client->net_proto_version >= 3)
4343 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4344 srp->m_removed = true;
4345 srp->m_respawn_active = true;
4349 RespawnPlayer(player);
4353 void Server::RespawnPlayer(Player *player)
4356 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4357 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4359 v3f pos = findSpawnPos(m_env->getServerMap());
4360 player->setPosition(pos);
4361 srp->m_last_good_position = pos;
4362 srp->m_last_good_position_age = 0;
4364 SendMovePlayer(player);
4365 SendPlayerHP(player);
4368 void Server::UpdateCrafting(u16 peer_id)
4370 DSTACK(__FUNCTION_NAME);
4372 Player* player = m_env->getPlayer(peer_id);
4374 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4376 // No crafting in creative mode
4377 if(g_settings->getBool("creative_mode"))
4380 // Get the InventoryLists of the player in which we will operate
4381 InventoryList *clist = player->inventory.getList("craft");
4383 InventoryList *rlist = player->inventory.getList("craftresult");
4385 InventoryList *mlist = player->inventory.getList("main");
4388 // If the result list is not a preview and is not empty, try to
4389 // throw the item into main list
4390 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4392 // Grab item out of craftresult
4393 InventoryItem *item = rlist->changeItem(0, NULL);
4394 // Try to put in main
4395 InventoryItem *leftover = mlist->addItem(item);
4396 // If there are leftovers, put them back to craftresult and
4398 delete rlist->addItem(leftover);
4399 // Inventory was modified
4400 srp->m_inventory_not_sent = true;
4403 // If result list is empty, we will make it preview what would be
4405 if(rlist->getUsedSlots() == 0)
4406 player->craftresult_is_preview = true;
4408 // If it is a preview, clear the possible old preview in it
4409 if(player->craftresult_is_preview)
4410 rlist->clearItems();
4412 // If it is a preview, find out what is the crafting result
4414 if(player->craftresult_is_preview)
4416 // Mangle crafting grid to an another format
4417 std::vector<InventoryItem*> items;
4418 for(u16 i=0; i<9; i++){
4419 if(clist->getItem(i) == NULL)
4420 items.push_back(NULL);
4422 items.push_back(clist->getItem(i)->clone());
4424 CraftPointerInput cpi(3, items);
4426 // Find out what is crafted and add it to result item slot
4427 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4429 rlist->addItem(result);
4433 RemoteClient* Server::getClient(u16 peer_id)
4435 DSTACK(__FUNCTION_NAME);
4436 //JMutexAutoLock lock(m_con_mutex);
4437 core::map<u16, RemoteClient*>::Node *n;
4438 n = m_clients.find(peer_id);
4439 // A client should exist for all peers
4441 return n->getValue();
4444 std::wstring Server::getStatusString()
4446 std::wostringstream os(std::ios_base::binary);
4449 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4451 os<<L", uptime="<<m_uptime.get();
4452 // Information about clients
4454 for(core::map<u16, RemoteClient*>::Iterator
4455 i = m_clients.getIterator();
4456 i.atEnd() == false; i++)
4458 // Get client and check that it is valid
4459 RemoteClient *client = i.getNode()->getValue();
4460 assert(client->peer_id == i.getNode()->getKey());
4461 if(client->serialization_version == SER_FMT_VER_INVALID)
4464 Player *player = m_env->getPlayer(client->peer_id);
4465 // Get name of player
4466 std::wstring name = L"unknown";
4468 name = narrow_to_wide(player->getName());
4469 // Add name to information string
4473 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4474 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4475 if(g_settings->get("motd") != "")
4476 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4480 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4482 // Add player to auth manager
4483 if(m_authmanager.exists(name) == false)
4485 infostream<<"Server: adding player "<<name
4486 <<" to auth manager"<<std::endl;
4487 m_authmanager.add(name);
4488 m_authmanager.setPrivs(name,
4489 stringToPrivs(g_settings->get("default_privs")));
4491 // Change password and save
4492 m_authmanager.setPassword(name, translatePassword(name, password));
4493 m_authmanager.save();
4496 // Saves g_settings to configpath given at initialization
4497 void Server::saveConfig()
4499 if(m_configpath != "")
4500 g_settings->updateConfigFile(m_configpath.c_str());
4503 void Server::notifyPlayer(const char *name, const std::wstring msg)
4505 Player *player = m_env->getPlayer(name);
4508 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4511 void Server::notifyPlayers(const std::wstring msg)
4513 BroadcastChatMessage(msg);
4516 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4520 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4521 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4524 // IGameDef interface
4526 IToolDefManager* Server::getToolDefManager()
4530 INodeDefManager* Server::getNodeDefManager()
4534 ICraftDefManager* Server::getCraftDefManager()
4538 ICraftItemDefManager* Server::getCraftItemDefManager()
4540 return m_craftitemdef;
4542 ITextureSource* Server::getTextureSource()
4546 u16 Server::allocateUnknownNodeId(const std::string &name)
4548 return m_nodedef->allocateDummy(name);
4551 IWritableToolDefManager* Server::getWritableToolDefManager()
4555 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4559 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4563 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4565 return m_craftitemdef;
4568 v3f findSpawnPos(ServerMap &map)
4570 //return v3f(50,50,50)*BS;
4575 nodepos = v2s16(0,0);
4580 // Try to find a good place a few times
4581 for(s32 i=0; i<1000; i++)
4584 // We're going to try to throw the player to this position
4585 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4586 -range + (myrand()%(range*2)));
4587 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4588 // Get ground height at point (fallbacks to heightmap function)
4589 s16 groundheight = map.findGroundLevel(nodepos2d);
4590 // Don't go underwater
4591 if(groundheight < WATER_LEVEL)
4593 //infostream<<"-> Underwater"<<std::endl;
4596 // Don't go to high places
4597 if(groundheight > WATER_LEVEL + 4)
4599 //infostream<<"-> Underwater"<<std::endl;
4603 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4604 bool is_good = false;
4606 for(s32 i=0; i<10; i++){
4607 v3s16 blockpos = getNodeBlockPos(nodepos);
4608 map.emergeBlock(blockpos, true);
4609 MapNode n = map.getNodeNoEx(nodepos);
4610 if(n.getContent() == CONTENT_AIR){
4621 // Found a good place
4622 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4628 return intToFloat(nodepos, BS);
4631 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4634 Try to get an existing player
4636 ServerRemotePlayer *player =
4637 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4640 // If player is already connected, cancel
4641 if(player->peer_id != 0)
4643 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4648 player->peer_id = peer_id;
4650 // Reset inventory to creative if in creative mode
4651 if(g_settings->getBool("creative_mode"))
4653 // Warning: double code below
4654 // Backup actual inventory
4655 player->inventory_backup = new Inventory();
4656 *(player->inventory_backup) = player->inventory;
4657 // Set creative inventory
4658 player->resetInventory();
4659 scriptapi_get_creative_inventory(m_lua, player);
4666 If player with the wanted peer_id already exists, cancel.
4668 if(m_env->getPlayer(peer_id) != NULL)
4670 infostream<<"emergePlayer(): Player with wrong name but same"
4671 " peer_id already exists"<<std::endl;
4679 /* Set player position */
4681 infostream<<"Server: Finding spawn place for player \""
4682 <<name<<"\""<<std::endl;
4684 v3f pos = findSpawnPos(m_env->getServerMap());
4686 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4688 /* Add player to environment */
4689 m_env->addPlayer(player);
4692 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4693 scriptapi_on_newplayer(m_lua, srp);
4695 /* Add stuff to inventory */
4696 if(g_settings->getBool("creative_mode"))
4698 // Warning: double code above
4699 // Backup actual inventory
4700 player->inventory_backup = new Inventory();
4701 *(player->inventory_backup) = player->inventory;
4702 // Set creative inventory
4703 player->resetInventory();
4704 scriptapi_get_creative_inventory(m_lua, player);
4709 } // create new player
4712 void Server::handlePeerChange(PeerChange &c)
4714 JMutexAutoLock envlock(m_env_mutex);
4715 JMutexAutoLock conlock(m_con_mutex);
4717 if(c.type == PEER_ADDED)
4724 core::map<u16, RemoteClient*>::Node *n;
4725 n = m_clients.find(c.peer_id);
4726 // The client shouldn't already exist
4730 RemoteClient *client = new RemoteClient();
4731 client->peer_id = c.peer_id;
4732 m_clients.insert(client->peer_id, client);
4735 else if(c.type == PEER_REMOVED)
4742 core::map<u16, RemoteClient*>::Node *n;
4743 n = m_clients.find(c.peer_id);
4744 // The client should exist
4748 Mark objects to be not known by the client
4750 RemoteClient *client = n->getValue();
4752 for(core::map<u16, bool>::Iterator
4753 i = client->m_known_objects.getIterator();
4754 i.atEnd()==false; i++)
4757 u16 id = i.getNode()->getKey();
4758 ServerActiveObject* obj = m_env->getActiveObject(id);
4760 if(obj && obj->m_known_by_count > 0)
4761 obj->m_known_by_count--;
4764 ServerRemotePlayer* player =
4765 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4767 // Collect information about leaving in chat
4768 std::wstring message;
4772 std::wstring name = narrow_to_wide(player->getName());
4775 message += L" left game";
4777 message += L" (timed out)";
4781 // Remove from environment
4783 player->m_removed = true;
4785 // Set player client disconnected
4787 player->peer_id = 0;
4795 std::ostringstream os(std::ios_base::binary);
4796 for(core::map<u16, RemoteClient*>::Iterator
4797 i = m_clients.getIterator();
4798 i.atEnd() == false; i++)
4800 RemoteClient *client = i.getNode()->getValue();
4801 assert(client->peer_id == i.getNode()->getKey());
4802 if(client->serialization_version == SER_FMT_VER_INVALID)
4805 Player *player = m_env->getPlayer(client->peer_id);
4808 // Get name of player
4809 os<<player->getName()<<" ";
4812 actionstream<<player->getName()<<" "
4813 <<(c.timeout?"times out.":"leaves game.")
4814 <<" List of players: "
4815 <<os.str()<<std::endl;
4820 delete m_clients[c.peer_id];
4821 m_clients.remove(c.peer_id);
4823 // Send player info to all remaining clients
4824 //SendPlayerInfos();
4826 // Send leave chat message to all remaining clients
4827 BroadcastChatMessage(message);
4836 void Server::handlePeerChanges()
4838 while(m_peer_change_queue.size() > 0)
4840 PeerChange c = m_peer_change_queue.pop_front();
4842 infostream<<"Server: Handling peer change: "
4843 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4846 handlePeerChange(c);
4850 u64 Server::getPlayerPrivs(Player *player)
4854 std::string playername = player->getName();
4855 // Local player gets all privileges regardless of
4856 // what's set on their account.
4857 if(g_settings->get("name") == playername)
4863 return getPlayerAuthPrivs(playername);
4867 void dedicated_server_loop(Server &server, bool &kill)
4869 DSTACK(__FUNCTION_NAME);
4871 infostream<<DTIME<<std::endl;
4872 infostream<<"========================"<<std::endl;
4873 infostream<<"Running dedicated server"<<std::endl;
4874 infostream<<"========================"<<std::endl;
4875 infostream<<std::endl;
4877 IntervalLimiter m_profiler_interval;
4881 // This is kind of a hack but can be done like this
4882 // because server.step() is very light
4884 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4889 if(server.getShutdownRequested() || kill)
4891 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4898 float profiler_print_interval =
4899 g_settings->getFloat("profiler_print_interval");
4900 if(profiler_print_interval != 0)
4902 if(m_profiler_interval.step(0.030, profiler_print_interval))
4904 infostream<<"Profiler:"<<std::endl;
4905 g_profiler->print(infostream);
4906 g_profiler->clear();
4913 static int counter = 0;
4919 core::list<PlayerInfo> list = server.getPlayerInfo();
4920 core::list<PlayerInfo>::Iterator i;
4921 static u32 sum_old = 0;
4922 u32 sum = PIChecksum(list);
4925 infostream<<DTIME<<"Player info:"<<std::endl;
4926 for(i=list.begin(); i!=list.end(); i++)
4928 i->PrintLine(&infostream);