3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
46 #include "content_tool.h" // For content_tool_init
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
240 if(enable_mapgen_debug_info)
241 infostream<<"EmergeThread: ended up with: "
242 <<analyze_block(block)<<std::endl;
251 Ignore map edit events, they will not need to be
252 sent to anybody because the block hasn't been sent
255 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
257 // Activate objects and stuff
258 m_server->m_env->activateBlock(block, 3600);
263 /*if(block->getLightingExpired()){
264 lighting_invalidated_blocks[block->getPos()] = block;
268 // TODO: Some additional checking and lighting updating,
273 JMutexAutoLock envlock(m_server->m_env_mutex);
278 Collect a list of blocks that have been modified in
279 addition to the fetched one.
283 if(lighting_invalidated_blocks.size() > 0)
285 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
286 <<" blocks"<<std::endl;*/
288 // 50-100ms for single block generation
289 //TimeTaker timer("** EmergeThread updateLighting");
291 // Update lighting without locking the environment mutex,
292 // add modified blocks to changed blocks
293 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
296 // Add all from changed_blocks to modified_blocks
297 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
298 i.atEnd() == false; i++)
300 MapBlock *block = i.getNode()->getValue();
301 modified_blocks.insert(block->getPos(), block);
305 // If we got no block, there should be no invalidated blocks
308 //assert(lighting_invalidated_blocks.size() == 0);
314 Set sent status of modified blocks on clients
317 // NOTE: Server's clients are also behind the connection mutex
318 JMutexAutoLock lock(m_server->m_con_mutex);
321 Add the originally fetched block to the modified list
325 modified_blocks.insert(p, block);
329 Set the modified blocks unsent for all the clients
332 for(core::map<u16, RemoteClient*>::Iterator
333 i = m_server->m_clients.getIterator();
334 i.atEnd() == false; i++)
336 RemoteClient *client = i.getNode()->getValue();
338 if(modified_blocks.size() > 0)
340 // Remove block from sent history
341 client->SetBlocksNotSent(modified_blocks);
347 END_DEBUG_EXCEPTION_HANDLER(errorstream)
352 void RemoteClient::GetNextBlocks(Server *server, float dtime,
353 core::array<PrioritySortedBlockTransfer> &dest)
355 DSTACK(__FUNCTION_NAME);
358 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
361 m_nothing_to_send_pause_timer -= dtime;
362 m_nearest_unsent_reset_timer += dtime;
364 if(m_nothing_to_send_pause_timer >= 0)
369 // Won't send anything if already sending
370 if(m_blocks_sending.size() >= g_settings->getU16
371 ("max_simultaneous_block_sends_per_client"))
373 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
377 //TimeTaker timer("RemoteClient::GetNextBlocks");
379 Player *player = server->m_env->getPlayer(peer_id);
381 assert(player != NULL);
383 v3f playerpos = player->getPosition();
384 v3f playerspeed = player->getSpeed();
385 v3f playerspeeddir(0,0,0);
386 if(playerspeed.getLength() > 1.0*BS)
387 playerspeeddir = playerspeed / playerspeed.getLength();
388 // Predict to next block
389 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
391 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
393 v3s16 center = getNodeBlockPos(center_nodepos);
395 // Camera position and direction
396 v3f camera_pos = player->getEyePosition();
397 v3f camera_dir = v3f(0,0,1);
398 camera_dir.rotateYZBy(player->getPitch());
399 camera_dir.rotateXZBy(player->getYaw());
401 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
402 <<camera_dir.Z<<")"<<std::endl;*/
405 Get the starting value of the block finder radius.
408 if(m_last_center != center)
410 m_nearest_unsent_d = 0;
411 m_last_center = center;
414 /*infostream<<"m_nearest_unsent_reset_timer="
415 <<m_nearest_unsent_reset_timer<<std::endl;*/
417 // Reset periodically to workaround for some bugs or stuff
418 if(m_nearest_unsent_reset_timer > 20.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 //infostream<<"Resetting m_nearest_unsent_d for "
423 // <<server->getPlayerName(peer_id)<<std::endl;
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 s16 max_d_increment_at_time = 2;
467 if(d_max > d_start + max_d_increment_at_time)
468 d_max = d_start + max_d_increment_at_time;
469 /*if(d_max_gen > d_start+2)
470 d_max_gen = d_start+2;*/
472 //infostream<<"Starting from "<<d_start<<std::endl;
474 s32 nearest_emerged_d = -1;
475 s32 nearest_emergefull_d = -1;
476 s32 nearest_sent_d = -1;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 /*errorstream<<"checking d="<<d<<" for "
483 <<server->getPlayerName(peer_id)<<std::endl;*/
484 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
487 If m_nearest_unsent_d was changed by the EmergeThread
488 (it can change it to 0 through SetBlockNotSent),
490 Else update m_nearest_unsent_d
492 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
494 d = m_nearest_unsent_d;
495 last_nearest_unsent_d = m_nearest_unsent_d;
499 Get the border/face dot coordinates of a "d-radiused"
502 core::list<v3s16> list;
503 getFacePositions(list, d);
505 core::list<v3s16>::Iterator li;
506 for(li=list.begin(); li!=list.end(); li++)
508 v3s16 p = *li + center;
512 - Don't allow too many simultaneous transfers
513 - EXCEPT when the blocks are very close
515 Also, don't send blocks that are already flying.
518 // Start with the usual maximum
519 u16 max_simul_dynamic = max_simul_sends_usually;
521 // If block is very close, allow full maximum
522 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
523 max_simul_dynamic = max_simul_sends_setting;
525 // Don't select too many blocks for sending
526 if(num_blocks_selected >= max_simul_dynamic)
528 queue_is_full = true;
529 goto queue_full_break;
532 // Don't send blocks that are currently being transferred
533 if(m_blocks_sending.find(p) != NULL)
539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
547 // If this is true, inexistent block will be made from scratch
548 bool generate = d <= d_max_gen;
551 /*// Limit the generating area vertically to 2/3
552 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
555 // Limit the send area vertically to 1/2
556 if(abs(p.Y - center.Y) > d_max / 2)
562 If block is far away, don't generate it unless it is
568 // Block center y in nodes
569 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 // Don't generate if it's very high or very low
571 if(y < -64 || y > 64)
575 v2s16 p2d_nodes_center(
579 // Get ground height in nodes
580 s16 gh = server->m_env->getServerMap().findGroundLevel(
583 // If differs a lot, don't generate
584 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
586 // Actually, don't even send it
592 //infostream<<"d="<<d<<std::endl;
595 Don't generate or send if not in sight
596 FIXME This only works if the client uses a small enough
597 FOV setting. The default of 72 degrees is fine.
600 float camera_fov = (72.0*PI/180) * 4./3.;
601 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
607 Don't send already sent blocks
610 if(m_blocks_sent.find(p) != NULL)
617 Check if map has this block
619 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
621 bool surely_not_found_on_disk = false;
622 bool block_is_invalid = false;
625 // Reset usage timer, this block will be of use in the future.
626 block->resetUsageTimer();
628 // Block is dummy if data doesn't exist.
629 // It means it has been not found from disk and not generated
632 surely_not_found_on_disk = true;
635 // Block is valid if lighting is up-to-date and data exists
636 if(block->isValid() == false)
638 block_is_invalid = true;
641 /*if(block->isFullyGenerated() == false)
643 block_is_invalid = true;
648 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
649 v2s16 chunkpos = map->sector_to_chunk(p2d);
650 if(map->chunkNonVolatile(chunkpos) == false)
651 block_is_invalid = true;
653 if(block->isGenerated() == false)
654 block_is_invalid = true;
657 If block is not close, don't send it unless it is near
660 Block is near ground level if night-time mesh
661 differs from day-time mesh.
665 if(block->dayNightDiffed() == false)
672 If block has been marked to not exist on disk (dummy)
673 and generating new ones is not wanted, skip block.
675 if(generate == false && surely_not_found_on_disk == true)
682 Add inexistent block to emerge queue.
684 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
686 //TODO: Get value from somewhere
687 // Allow only one block in emerge queue
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
689 // Allow two blocks in queue per client
690 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
691 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
693 //infostream<<"Adding block to emerge queue"<<std::endl;
695 // Add it to the emerge queue and trigger the thread
698 if(generate == false)
699 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
701 server->m_emerge_queue.addBlock(peer_id, p, flags);
702 server->m_emergethread.trigger();
704 if(nearest_emerged_d == -1)
705 nearest_emerged_d = d;
707 if(nearest_emergefull_d == -1)
708 nearest_emergefull_d = d;
715 if(nearest_sent_d == -1)
719 Add block to send queue
722 /*errorstream<<"sending from d="<<d<<" to "
723 <<server->getPlayerName(peer_id)<<std::endl;*/
725 PrioritySortedBlockTransfer q((float)d, p, peer_id);
729 num_blocks_selected += 1;
734 //infostream<<"Stopped at "<<d<<std::endl;
736 // If nothing was found for sending and nothing was queued for
737 // emerging, continue next time browsing from here
738 if(nearest_emerged_d != -1){
739 new_nearest_unsent_d = nearest_emerged_d;
740 } else if(nearest_emergefull_d != -1){
741 new_nearest_unsent_d = nearest_emergefull_d;
743 if(d > g_settings->getS16("max_block_send_distance")){
744 new_nearest_unsent_d = 0;
745 m_nothing_to_send_pause_timer = 2.0;
746 /*infostream<<"GetNextBlocks(): d wrapped around for "
747 <<server->getPlayerName(peer_id)
748 <<"; setting to 0 and pausing"<<std::endl;*/
750 if(nearest_sent_d != -1)
751 new_nearest_unsent_d = nearest_sent_d;
753 new_nearest_unsent_d = d;
757 if(new_nearest_unsent_d != -1)
758 m_nearest_unsent_d = new_nearest_unsent_d;
760 /*timer_result = timer.stop(true);
761 if(timer_result != 0)
762 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
765 void RemoteClient::SendObjectData(
768 core::map<v3s16, bool> &stepped_blocks
771 DSTACK(__FUNCTION_NAME);
773 // Can't send anything without knowing version
774 if(serialization_version == SER_FMT_VER_INVALID)
776 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
782 Send a TOCLIENT_OBJECTDATA packet.
786 u16 number of player positions
798 std::ostringstream os(std::ios_base::binary);
802 writeU16(buf, TOCLIENT_OBJECTDATA);
803 os.write((char*)buf, 2);
806 Get and write player data
809 // Get connected players
810 core::list<Player*> players = server->m_env->getPlayers(true);
812 // Write player count
813 u16 playercount = players.size();
814 writeU16(buf, playercount);
815 os.write((char*)buf, 2);
817 core::list<Player*>::Iterator i;
818 for(i = players.begin();
819 i != players.end(); i++)
823 v3f pf = player->getPosition();
824 v3f sf = player->getSpeed();
826 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
827 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
828 s32 pitch_i (player->getPitch() * 100);
829 s32 yaw_i (player->getYaw() * 100);
831 writeU16(buf, player->peer_id);
832 os.write((char*)buf, 2);
833 writeV3S32(buf, position_i);
834 os.write((char*)buf, 12);
835 writeV3S32(buf, speed_i);
836 os.write((char*)buf, 12);
837 writeS32(buf, pitch_i);
838 os.write((char*)buf, 4);
839 writeS32(buf, yaw_i);
840 os.write((char*)buf, 4);
844 Get and write object data (dummy, for compatibility)
849 os.write((char*)buf, 2);
855 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
858 std::string s = os.str();
859 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
860 // Send as unreliable
861 server->m_con.Send(peer_id, 0, data, false);
864 void RemoteClient::GotBlock(v3s16 p)
866 if(m_blocks_sending.find(p) != NULL)
867 m_blocks_sending.remove(p);
870 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
871 " m_blocks_sending"<<std::endl;*/
872 m_excess_gotblocks++;
874 m_blocks_sent.insert(p, true);
877 void RemoteClient::SentBlock(v3s16 p)
879 if(m_blocks_sending.find(p) == NULL)
880 m_blocks_sending.insert(p, 0.0);
882 infostream<<"RemoteClient::SentBlock(): Sent block"
883 " already in m_blocks_sending"<<std::endl;
886 void RemoteClient::SetBlockNotSent(v3s16 p)
888 m_nearest_unsent_d = 0;
890 if(m_blocks_sending.find(p) != NULL)
891 m_blocks_sending.remove(p);
892 if(m_blocks_sent.find(p) != NULL)
893 m_blocks_sent.remove(p);
896 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
898 m_nearest_unsent_d = 0;
900 for(core::map<v3s16, MapBlock*>::Iterator
901 i = blocks.getIterator();
902 i.atEnd()==false; i++)
904 v3s16 p = i.getNode()->getKey();
906 if(m_blocks_sending.find(p) != NULL)
907 m_blocks_sending.remove(p);
908 if(m_blocks_sent.find(p) != NULL)
909 m_blocks_sent.remove(p);
917 PlayerInfo::PlayerInfo()
923 void PlayerInfo::PrintLine(std::ostream *s)
926 (*s)<<"\""<<name<<"\" ("
927 <<(position.X/10)<<","<<(position.Y/10)
928 <<","<<(position.Z/10)<<") ";
930 (*s)<<" avg_rtt="<<avg_rtt;
934 u32 PIChecksum(core::list<PlayerInfo> &l)
936 core::list<PlayerInfo>::Iterator i;
939 for(i=l.begin(); i!=l.end(); i++)
941 checksum += a * (i->id+1);
942 checksum ^= 0x435aafcd;
953 std::string mapsavedir,
954 std::string configpath
957 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
958 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
959 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
961 m_toolmgr(createToolDefManager()),
962 m_nodemgr(createNodeDefManager(NULL)),
964 m_emergethread(this),
966 m_time_of_day_send_timer(0),
968 m_mapsavedir(mapsavedir),
969 m_configpath(configpath),
970 m_shutdown_requested(false),
971 m_ignore_map_edit_events(false),
972 m_ignore_map_edit_events_peer_id(0)
974 m_liquid_transform_timer = 0.0;
975 m_print_info_timer = 0.0;
976 m_objectdata_timer = 0.0;
977 m_emergethread_trigger_timer = 0.0;
978 m_savemap_timer = 0.0;
982 m_step_dtime_mutex.Init();
985 JMutexAutoLock envlock(m_env_mutex);
986 JMutexAutoLock conlock(m_con_mutex);
988 infostream<<"m_nodemgr="<<m_nodemgr<<std::endl;
990 // Initialize default tool definitions
991 content_tool_init(m_toolmgr);
993 // Initialize default node definitions
994 content_mapnode_init(NULL, m_nodemgr);
996 // Initialize scripting
998 infostream<<"Server: Initializing scripting"<<std::endl;
999 m_lua = script_init();
1002 scriptapi_export(m_lua, this);
1003 // Load and run scripts
1004 std::string defaultscript = porting::path_data + DIR_DELIM
1005 + "scripts" + DIR_DELIM + "default.lua";
1006 bool success = script_load(m_lua, defaultscript.c_str());
1008 errorstream<<"Server: Failed to load and run "
1009 <<defaultscript<<std::endl;
1013 // Initialize Environment
1015 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1017 // Give environment reference to scripting api
1018 scriptapi_add_environment(m_lua, m_env);
1020 // Register us to receive map edit events
1021 m_env->getMap().addEventReceiver(this);
1023 // If file exists, load environment metadata
1024 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1026 infostream<<"Server: Loading environment metadata"<<std::endl;
1027 m_env->loadMeta(m_mapsavedir);
1031 infostream<<"Server: Loading players"<<std::endl;
1032 m_env->deSerializePlayers(m_mapsavedir);
1037 infostream<<"Server::~Server()"<<std::endl;
1040 Send shutdown message
1043 JMutexAutoLock conlock(m_con_mutex);
1045 std::wstring line = L"*** Server shutting down";
1048 Send the message to clients
1050 for(core::map<u16, RemoteClient*>::Iterator
1051 i = m_clients.getIterator();
1052 i.atEnd() == false; i++)
1054 // Get client and check that it is valid
1055 RemoteClient *client = i.getNode()->getValue();
1056 assert(client->peer_id == i.getNode()->getKey());
1057 if(client->serialization_version == SER_FMT_VER_INVALID)
1061 SendChatMessage(client->peer_id, line);
1063 catch(con::PeerNotFoundException &e)
1069 JMutexAutoLock envlock(m_env_mutex);
1074 infostream<<"Server: Saving players"<<std::endl;
1075 m_env->serializePlayers(m_mapsavedir);
1078 Save environment metadata
1080 infostream<<"Server: Saving environment metadata"<<std::endl;
1081 m_env->saveMeta(m_mapsavedir);
1093 JMutexAutoLock clientslock(m_con_mutex);
1095 for(core::map<u16, RemoteClient*>::Iterator
1096 i = m_clients.getIterator();
1097 i.atEnd() == false; i++)
1100 // NOTE: These are removed by env destructor
1102 u16 peer_id = i.getNode()->getKey();
1103 JMutexAutoLock envlock(m_env_mutex);
1104 m_env->removePlayer(peer_id);
1108 delete i.getNode()->getValue();
1112 // Delete Environment
1118 // Deinitialize scripting
1119 infostream<<"Server: Deinitializing scripting"<<std::endl;
1120 script_deinit(m_lua);
1123 void Server::start(unsigned short port)
1125 DSTACK(__FUNCTION_NAME);
1126 // Stop thread if already running
1129 // Initialize connection
1130 m_con.SetTimeoutMs(30);
1134 m_thread.setRun(true);
1137 infostream<<"Server: Started on port "<<port<<std::endl;
1142 DSTACK(__FUNCTION_NAME);
1144 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1146 // Stop threads (set run=false first so both start stopping)
1147 m_thread.setRun(false);
1148 m_emergethread.setRun(false);
1150 m_emergethread.stop();
1152 infostream<<"Server: Threads stopped"<<std::endl;
1155 void Server::step(float dtime)
1157 DSTACK(__FUNCTION_NAME);
1162 JMutexAutoLock lock(m_step_dtime_mutex);
1163 m_step_dtime += dtime;
1167 void Server::AsyncRunStep()
1169 DSTACK(__FUNCTION_NAME);
1171 g_profiler->add("Server::AsyncRunStep (num)", 1);
1175 JMutexAutoLock lock1(m_step_dtime_mutex);
1176 dtime = m_step_dtime;
1180 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1181 // Send blocks to clients
1188 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1190 //infostream<<"Server steps "<<dtime<<std::endl;
1191 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1194 JMutexAutoLock lock1(m_step_dtime_mutex);
1195 m_step_dtime -= dtime;
1202 m_uptime.set(m_uptime.get() + dtime);
1206 // Process connection's timeouts
1207 JMutexAutoLock lock2(m_con_mutex);
1208 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1209 m_con.RunTimeouts(dtime);
1213 // This has to be called so that the client list gets synced
1214 // with the peer list of the connection
1215 handlePeerChanges();
1219 Update m_time_of_day and overall game time
1222 JMutexAutoLock envlock(m_env_mutex);
1224 m_time_counter += dtime;
1225 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1226 u32 units = (u32)(m_time_counter*speed);
1227 m_time_counter -= (f32)units / speed;
1229 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1231 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1234 Send to clients at constant intervals
1237 m_time_of_day_send_timer -= dtime;
1238 if(m_time_of_day_send_timer < 0.0)
1240 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1242 //JMutexAutoLock envlock(m_env_mutex);
1243 JMutexAutoLock conlock(m_con_mutex);
1245 for(core::map<u16, RemoteClient*>::Iterator
1246 i = m_clients.getIterator();
1247 i.atEnd() == false; i++)
1249 RemoteClient *client = i.getNode()->getValue();
1250 //Player *player = m_env->getPlayer(client->peer_id);
1252 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1253 m_env->getTimeOfDay());
1255 m_con.Send(client->peer_id, 0, data, true);
1261 JMutexAutoLock lock(m_env_mutex);
1263 ScopeProfiler sp(g_profiler, "SEnv step");
1264 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1268 const float map_timer_and_unload_dtime = 5.15;
1269 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1271 JMutexAutoLock lock(m_env_mutex);
1272 // Run Map's timers and unload unused data
1273 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1274 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1275 g_settings->getFloat("server_unload_unused_data_timeout"));
1285 m_liquid_transform_timer += dtime;
1286 if(m_liquid_transform_timer >= 1.00)
1288 m_liquid_transform_timer -= 1.00;
1290 JMutexAutoLock lock(m_env_mutex);
1292 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1294 core::map<v3s16, MapBlock*> modified_blocks;
1295 m_env->getMap().transformLiquids(modified_blocks);
1300 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1301 ServerMap &map = ((ServerMap&)m_env->getMap());
1302 map.updateLighting(modified_blocks, lighting_modified_blocks);
1304 // Add blocks modified by lighting to modified_blocks
1305 for(core::map<v3s16, MapBlock*>::Iterator
1306 i = lighting_modified_blocks.getIterator();
1307 i.atEnd() == false; i++)
1309 MapBlock *block = i.getNode()->getValue();
1310 modified_blocks.insert(block->getPos(), block);
1314 Set the modified blocks unsent for all the clients
1317 JMutexAutoLock lock2(m_con_mutex);
1319 for(core::map<u16, RemoteClient*>::Iterator
1320 i = m_clients.getIterator();
1321 i.atEnd() == false; i++)
1323 RemoteClient *client = i.getNode()->getValue();
1325 if(modified_blocks.size() > 0)
1327 // Remove block from sent history
1328 client->SetBlocksNotSent(modified_blocks);
1333 // Periodically print some info
1335 float &counter = m_print_info_timer;
1341 JMutexAutoLock lock2(m_con_mutex);
1343 if(m_clients.size() != 0)
1344 infostream<<"Players:"<<std::endl;
1345 for(core::map<u16, RemoteClient*>::Iterator
1346 i = m_clients.getIterator();
1347 i.atEnd() == false; i++)
1349 //u16 peer_id = i.getNode()->getKey();
1350 RemoteClient *client = i.getNode()->getValue();
1351 Player *player = m_env->getPlayer(client->peer_id);
1354 infostream<<"* "<<player->getName()<<"\t";
1355 client->PrintInfo(infostream);
1360 //if(g_settings->getBool("enable_experimental"))
1364 Check added and deleted active objects
1367 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1368 JMutexAutoLock envlock(m_env_mutex);
1369 JMutexAutoLock conlock(m_con_mutex);
1371 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1373 // Radius inside which objects are active
1374 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1375 radius *= MAP_BLOCKSIZE;
1377 for(core::map<u16, RemoteClient*>::Iterator
1378 i = m_clients.getIterator();
1379 i.atEnd() == false; i++)
1381 RemoteClient *client = i.getNode()->getValue();
1382 Player *player = m_env->getPlayer(client->peer_id);
1385 // This can happen if the client timeouts somehow
1386 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1388 <<" has no associated player"<<std::endl;*/
1391 v3s16 pos = floatToInt(player->getPosition(), BS);
1393 core::map<u16, bool> removed_objects;
1394 core::map<u16, bool> added_objects;
1395 m_env->getRemovedActiveObjects(pos, radius,
1396 client->m_known_objects, removed_objects);
1397 m_env->getAddedActiveObjects(pos, radius,
1398 client->m_known_objects, added_objects);
1400 // Ignore if nothing happened
1401 if(removed_objects.size() == 0 && added_objects.size() == 0)
1403 //infostream<<"active objects: none changed"<<std::endl;
1407 std::string data_buffer;
1411 // Handle removed objects
1412 writeU16((u8*)buf, removed_objects.size());
1413 data_buffer.append(buf, 2);
1414 for(core::map<u16, bool>::Iterator
1415 i = removed_objects.getIterator();
1416 i.atEnd()==false; i++)
1419 u16 id = i.getNode()->getKey();
1420 ServerActiveObject* obj = m_env->getActiveObject(id);
1422 // Add to data buffer for sending
1423 writeU16((u8*)buf, i.getNode()->getKey());
1424 data_buffer.append(buf, 2);
1426 // Remove from known objects
1427 client->m_known_objects.remove(i.getNode()->getKey());
1429 if(obj && obj->m_known_by_count > 0)
1430 obj->m_known_by_count--;
1433 // Handle added objects
1434 writeU16((u8*)buf, added_objects.size());
1435 data_buffer.append(buf, 2);
1436 for(core::map<u16, bool>::Iterator
1437 i = added_objects.getIterator();
1438 i.atEnd()==false; i++)
1441 u16 id = i.getNode()->getKey();
1442 ServerActiveObject* obj = m_env->getActiveObject(id);
1445 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1447 infostream<<"WARNING: "<<__FUNCTION_NAME
1448 <<": NULL object"<<std::endl;
1450 type = obj->getType();
1452 // Add to data buffer for sending
1453 writeU16((u8*)buf, id);
1454 data_buffer.append(buf, 2);
1455 writeU8((u8*)buf, type);
1456 data_buffer.append(buf, 1);
1459 data_buffer.append(serializeLongString(
1460 obj->getClientInitializationData()));
1462 data_buffer.append(serializeLongString(""));
1464 // Add to known objects
1465 client->m_known_objects.insert(i.getNode()->getKey(), false);
1468 obj->m_known_by_count++;
1472 SharedBuffer<u8> reply(2 + data_buffer.size());
1473 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1474 memcpy((char*)&reply[2], data_buffer.c_str(),
1475 data_buffer.size());
1477 m_con.Send(client->peer_id, 0, reply, true);
1479 infostream<<"Server: Sent object remove/add: "
1480 <<removed_objects.size()<<" removed, "
1481 <<added_objects.size()<<" added, "
1482 <<"packet size is "<<reply.getSize()<<std::endl;
1487 Collect a list of all the objects known by the clients
1488 and report it back to the environment.
1491 core::map<u16, bool> all_known_objects;
1493 for(core::map<u16, RemoteClient*>::Iterator
1494 i = m_clients.getIterator();
1495 i.atEnd() == false; i++)
1497 RemoteClient *client = i.getNode()->getValue();
1498 // Go through all known objects of client
1499 for(core::map<u16, bool>::Iterator
1500 i = client->m_known_objects.getIterator();
1501 i.atEnd()==false; i++)
1503 u16 id = i.getNode()->getKey();
1504 all_known_objects[id] = true;
1508 m_env->setKnownActiveObjects(whatever);
1514 Send object messages
1517 JMutexAutoLock envlock(m_env_mutex);
1518 JMutexAutoLock conlock(m_con_mutex);
1520 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1523 // Value = data sent by object
1524 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1526 // Get active object messages from environment
1529 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1533 core::list<ActiveObjectMessage>* message_list = NULL;
1534 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1535 n = buffered_messages.find(aom.id);
1538 message_list = new core::list<ActiveObjectMessage>;
1539 buffered_messages.insert(aom.id, message_list);
1543 message_list = n->getValue();
1545 message_list->push_back(aom);
1548 // Route data to every client
1549 for(core::map<u16, RemoteClient*>::Iterator
1550 i = m_clients.getIterator();
1551 i.atEnd()==false; i++)
1553 RemoteClient *client = i.getNode()->getValue();
1554 std::string reliable_data;
1555 std::string unreliable_data;
1556 // Go through all objects in message buffer
1557 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1558 j = buffered_messages.getIterator();
1559 j.atEnd()==false; j++)
1561 // If object is not known by client, skip it
1562 u16 id = j.getNode()->getKey();
1563 if(client->m_known_objects.find(id) == NULL)
1565 // Get message list of object
1566 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1567 // Go through every message
1568 for(core::list<ActiveObjectMessage>::Iterator
1569 k = list->begin(); k != list->end(); k++)
1571 // Compose the full new data with header
1572 ActiveObjectMessage aom = *k;
1573 std::string new_data;
1576 writeU16((u8*)&buf[0], aom.id);
1577 new_data.append(buf, 2);
1579 new_data += serializeString(aom.datastring);
1580 // Add data to buffer
1582 reliable_data += new_data;
1584 unreliable_data += new_data;
1588 reliable_data and unreliable_data are now ready.
1591 if(reliable_data.size() > 0)
1593 SharedBuffer<u8> reply(2 + reliable_data.size());
1594 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1595 memcpy((char*)&reply[2], reliable_data.c_str(),
1596 reliable_data.size());
1598 m_con.Send(client->peer_id, 0, reply, true);
1600 if(unreliable_data.size() > 0)
1602 SharedBuffer<u8> reply(2 + unreliable_data.size());
1603 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1604 memcpy((char*)&reply[2], unreliable_data.c_str(),
1605 unreliable_data.size());
1606 // Send as unreliable
1607 m_con.Send(client->peer_id, 0, reply, false);
1610 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1612 infostream<<"Server: Size of object message data: "
1613 <<"reliable: "<<reliable_data.size()
1614 <<", unreliable: "<<unreliable_data.size()
1619 // Clear buffered_messages
1620 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1621 i = buffered_messages.getIterator();
1622 i.atEnd()==false; i++)
1624 delete i.getNode()->getValue();
1628 } // enable_experimental
1631 Send queued-for-sending map edit events.
1634 // Don't send too many at a time
1637 // Single change sending is disabled if queue size is not small
1638 bool disable_single_change_sending = false;
1639 if(m_unsent_map_edit_queue.size() >= 4)
1640 disable_single_change_sending = true;
1642 bool got_any_events = false;
1644 // We'll log the amount of each
1647 while(m_unsent_map_edit_queue.size() != 0)
1649 got_any_events = true;
1651 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1653 // Players far away from the change are stored here.
1654 // Instead of sending the changes, MapBlocks are set not sent
1656 core::list<u16> far_players;
1658 if(event->type == MEET_ADDNODE)
1660 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1661 prof.add("MEET_ADDNODE", 1);
1662 if(disable_single_change_sending)
1663 sendAddNode(event->p, event->n, event->already_known_by_peer,
1666 sendAddNode(event->p, event->n, event->already_known_by_peer,
1669 else if(event->type == MEET_REMOVENODE)
1671 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1672 prof.add("MEET_REMOVENODE", 1);
1673 if(disable_single_change_sending)
1674 sendRemoveNode(event->p, event->already_known_by_peer,
1677 sendRemoveNode(event->p, event->already_known_by_peer,
1680 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1682 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1683 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1684 setBlockNotSent(event->p);
1686 else if(event->type == MEET_OTHER)
1688 infostream<<"Server: MEET_OTHER"<<std::endl;
1689 prof.add("MEET_OTHER", 1);
1690 for(core::map<v3s16, bool>::Iterator
1691 i = event->modified_blocks.getIterator();
1692 i.atEnd()==false; i++)
1694 v3s16 p = i.getNode()->getKey();
1700 prof.add("unknown", 1);
1701 infostream<<"WARNING: Server: Unknown MapEditEvent "
1702 <<((u32)event->type)<<std::endl;
1706 Set blocks not sent to far players
1708 if(far_players.size() > 0)
1710 // Convert list format to that wanted by SetBlocksNotSent
1711 core::map<v3s16, MapBlock*> modified_blocks2;
1712 for(core::map<v3s16, bool>::Iterator
1713 i = event->modified_blocks.getIterator();
1714 i.atEnd()==false; i++)
1716 v3s16 p = i.getNode()->getKey();
1717 modified_blocks2.insert(p,
1718 m_env->getMap().getBlockNoCreateNoEx(p));
1720 // Set blocks not sent
1721 for(core::list<u16>::Iterator
1722 i = far_players.begin();
1723 i != far_players.end(); i++)
1726 RemoteClient *client = getClient(peer_id);
1729 client->SetBlocksNotSent(modified_blocks2);
1735 /*// Don't send too many at a time
1737 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1743 infostream<<"Server: MapEditEvents:"<<std::endl;
1744 prof.print(infostream);
1750 Send object positions
1753 float &counter = m_objectdata_timer;
1755 if(counter >= g_settings->getFloat("objectdata_interval"))
1757 JMutexAutoLock lock1(m_env_mutex);
1758 JMutexAutoLock lock2(m_con_mutex);
1760 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1762 SendObjectData(counter);
1769 Trigger emergethread (it somehow gets to a non-triggered but
1770 bysy state sometimes)
1773 float &counter = m_emergethread_trigger_timer;
1779 m_emergethread.trigger();
1783 // Save map, players and auth stuff
1785 float &counter = m_savemap_timer;
1787 if(counter >= g_settings->getFloat("server_map_save_interval"))
1791 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1794 if(m_authmanager.isModified())
1795 m_authmanager.save();
1798 if(m_banmanager.isModified())
1799 m_banmanager.save();
1802 JMutexAutoLock lock(m_env_mutex);
1804 /*// Unload unused data (delete from memory)
1805 m_env->getMap().unloadUnusedData(
1806 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1808 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1809 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1812 // Save only changed parts
1813 m_env->getMap().save(true);
1815 /*if(deleted_count > 0)
1817 infostream<<"Server: Unloaded "<<deleted_count
1818 <<" blocks from memory"<<std::endl;
1822 m_env->serializePlayers(m_mapsavedir);
1824 // Save environment metadata
1825 m_env->saveMeta(m_mapsavedir);
1830 void Server::Receive()
1832 DSTACK(__FUNCTION_NAME);
1833 SharedBuffer<u8> data;
1838 JMutexAutoLock conlock(m_con_mutex);
1839 datasize = m_con.Receive(peer_id, data);
1842 // This has to be called so that the client list gets synced
1843 // with the peer list of the connection
1844 handlePeerChanges();
1846 ProcessData(*data, datasize, peer_id);
1848 catch(con::InvalidIncomingDataException &e)
1850 infostream<<"Server::Receive(): "
1851 "InvalidIncomingDataException: what()="
1852 <<e.what()<<std::endl;
1854 catch(con::PeerNotFoundException &e)
1856 //NOTE: This is not needed anymore
1858 // The peer has been disconnected.
1859 // Find the associated player and remove it.
1861 /*JMutexAutoLock envlock(m_env_mutex);
1863 infostream<<"ServerThread: peer_id="<<peer_id
1864 <<" has apparently closed connection. "
1865 <<"Removing player."<<std::endl;
1867 m_env->removePlayer(peer_id);*/
1871 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1873 DSTACK(__FUNCTION_NAME);
1874 // Environment is locked first.
1875 JMutexAutoLock envlock(m_env_mutex);
1876 JMutexAutoLock conlock(m_con_mutex);
1879 Address address = m_con.GetPeerAddress(peer_id);
1881 // drop player if is ip is banned
1882 if(m_banmanager.isIpBanned(address.serializeString())){
1883 SendAccessDenied(m_con, peer_id,
1884 L"Your ip is banned. Banned name was "
1885 +narrow_to_wide(m_banmanager.getBanName(
1886 address.serializeString())));
1887 m_con.DeletePeer(peer_id);
1891 catch(con::PeerNotFoundException &e)
1893 infostream<<"Server::ProcessData(): Cancelling: peer "
1894 <<peer_id<<" not found"<<std::endl;
1898 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1906 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1908 if(command == TOSERVER_INIT)
1910 // [0] u16 TOSERVER_INIT
1911 // [2] u8 SER_FMT_VER_HIGHEST
1912 // [3] u8[20] player_name
1913 // [23] u8[28] password <--- can be sent without this, from old versions
1915 if(datasize < 2+1+PLAYERNAME_SIZE)
1918 infostream<<"Server: Got TOSERVER_INIT from "
1919 <<peer_id<<std::endl;
1921 // First byte after command is maximum supported
1922 // serialization version
1923 u8 client_max = data[2];
1924 u8 our_max = SER_FMT_VER_HIGHEST;
1925 // Use the highest version supported by both
1926 u8 deployed = core::min_(client_max, our_max);
1927 // If it's lower than the lowest supported, give up.
1928 if(deployed < SER_FMT_VER_LOWEST)
1929 deployed = SER_FMT_VER_INVALID;
1931 //peer->serialization_version = deployed;
1932 getClient(peer_id)->pending_serialization_version = deployed;
1934 if(deployed == SER_FMT_VER_INVALID)
1936 infostream<<"Server: Cannot negotiate "
1937 "serialization version with peer "
1938 <<peer_id<<std::endl;
1939 SendAccessDenied(m_con, peer_id,
1940 L"Your client is too old (map format)");
1945 Read and check network protocol version
1948 u16 net_proto_version = 0;
1949 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1951 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1954 getClient(peer_id)->net_proto_version = net_proto_version;
1956 if(net_proto_version == 0)
1958 SendAccessDenied(m_con, peer_id,
1959 L"Your client is too old. Please upgrade.");
1963 /* Uhh... this should actually be a warning but let's do it like this */
1964 if(g_settings->getBool("strict_protocol_version_checking"))
1966 if(net_proto_version < PROTOCOL_VERSION)
1968 SendAccessDenied(m_con, peer_id,
1969 L"Your client is too old. Please upgrade.");
1979 char playername[PLAYERNAME_SIZE];
1980 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1982 playername[i] = data[3+i];
1984 playername[PLAYERNAME_SIZE-1] = 0;
1986 if(playername[0]=='\0')
1988 infostream<<"Server: Player has empty name"<<std::endl;
1989 SendAccessDenied(m_con, peer_id,
1994 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1996 infostream<<"Server: Player has invalid name"<<std::endl;
1997 SendAccessDenied(m_con, peer_id,
1998 L"Name contains unallowed characters");
2003 char password[PASSWORD_SIZE];
2004 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2006 // old version - assume blank password
2011 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2013 password[i] = data[23+i];
2015 password[PASSWORD_SIZE-1] = 0;
2018 std::string checkpwd;
2019 if(m_authmanager.exists(playername))
2021 checkpwd = m_authmanager.getPassword(playername);
2025 checkpwd = g_settings->get("default_password");
2028 /*infostream<<"Server: Client gave password '"<<password
2029 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2031 if(password != checkpwd && m_authmanager.exists(playername))
2033 infostream<<"Server: peer_id="<<peer_id
2034 <<": supplied invalid password for "
2035 <<playername<<std::endl;
2036 SendAccessDenied(m_con, peer_id, L"Invalid password");
2040 // Add player to auth manager
2041 if(m_authmanager.exists(playername) == false)
2043 infostream<<"Server: adding player "<<playername
2044 <<" to auth manager"<<std::endl;
2045 m_authmanager.add(playername);
2046 m_authmanager.setPassword(playername, checkpwd);
2047 m_authmanager.setPrivs(playername,
2048 stringToPrivs(g_settings->get("default_privs")));
2049 m_authmanager.save();
2052 // Enforce user limit.
2053 // Don't enforce for users that have some admin right
2054 if(m_clients.size() >= g_settings->getU16("max_users") &&
2055 (m_authmanager.getPrivs(playername)
2056 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2057 playername != g_settings->get("name"))
2059 SendAccessDenied(m_con, peer_id, L"Too many users.");
2064 Player *player = emergePlayer(playername, password, peer_id);
2066 // If failed, cancel
2069 infostream<<"Server: peer_id="<<peer_id
2070 <<": failed to emerge player"<<std::endl;
2075 Answer with a TOCLIENT_INIT
2078 SharedBuffer<u8> reply(2+1+6+8);
2079 writeU16(&reply[0], TOCLIENT_INIT);
2080 writeU8(&reply[2], deployed);
2081 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2082 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2085 m_con.Send(peer_id, 0, reply, true);
2089 Send complete position information
2091 SendMovePlayer(player);
2096 if(command == TOSERVER_INIT2)
2098 infostream<<"Server: Got TOSERVER_INIT2 from "
2099 <<peer_id<<std::endl;
2102 getClient(peer_id)->serialization_version
2103 = getClient(peer_id)->pending_serialization_version;
2106 Send some initialization data
2109 // Send player info to all players
2112 // Send inventory to player
2113 UpdateCrafting(peer_id);
2114 SendInventory(peer_id);
2116 // Send player items to all players
2119 Player *player = m_env->getPlayer(peer_id);
2122 SendPlayerHP(player);
2126 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2127 m_env->getTimeOfDay());
2128 m_con.Send(peer_id, 0, data, true);
2131 // Send information about server to player in chat
2132 SendChatMessage(peer_id, getStatusString());
2134 // Send information about joining in chat
2136 std::wstring name = L"unknown";
2137 Player *player = m_env->getPlayer(peer_id);
2139 name = narrow_to_wide(player->getName());
2141 std::wstring message;
2144 message += L" joined game";
2145 BroadcastChatMessage(message);
2148 // Warnings about protocol version can be issued here
2149 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2151 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2155 Check HP, respawn if necessary
2157 HandlePlayerHP(player, 0);
2163 std::ostringstream os(std::ios_base::binary);
2164 for(core::map<u16, RemoteClient*>::Iterator
2165 i = m_clients.getIterator();
2166 i.atEnd() == false; i++)
2168 RemoteClient *client = i.getNode()->getValue();
2169 assert(client->peer_id == i.getNode()->getKey());
2170 if(client->serialization_version == SER_FMT_VER_INVALID)
2173 Player *player = m_env->getPlayer(client->peer_id);
2176 // Get name of player
2177 os<<player->getName()<<" ";
2180 actionstream<<player->getName()<<" joins game. List of players: "
2181 <<os.str()<<std::endl;
2187 if(peer_ser_ver == SER_FMT_VER_INVALID)
2189 infostream<<"Server::ProcessData(): Cancelling: Peer"
2190 " serialization format invalid or not initialized."
2191 " Skipping incoming command="<<command<<std::endl;
2195 Player *player = m_env->getPlayer(peer_id);
2198 infostream<<"Server::ProcessData(): Cancelling: "
2199 "No player for peer_id="<<peer_id
2203 if(command == TOSERVER_PLAYERPOS)
2205 if(datasize < 2+12+12+4+4)
2209 v3s32 ps = readV3S32(&data[start+2]);
2210 v3s32 ss = readV3S32(&data[start+2+12]);
2211 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2212 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2213 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2214 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2215 pitch = wrapDegrees(pitch);
2216 yaw = wrapDegrees(yaw);
2218 player->setPosition(position);
2219 player->setSpeed(speed);
2220 player->setPitch(pitch);
2221 player->setYaw(yaw);
2223 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2224 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2225 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2227 else if(command == TOSERVER_GOTBLOCKS)
2240 u16 count = data[2];
2241 for(u16 i=0; i<count; i++)
2243 if((s16)datasize < 2+1+(i+1)*6)
2244 throw con::InvalidIncomingDataException
2245 ("GOTBLOCKS length is too short");
2246 v3s16 p = readV3S16(&data[2+1+i*6]);
2247 /*infostream<<"Server: GOTBLOCKS ("
2248 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2249 RemoteClient *client = getClient(peer_id);
2250 client->GotBlock(p);
2253 else if(command == TOSERVER_DELETEDBLOCKS)
2266 u16 count = data[2];
2267 for(u16 i=0; i<count; i++)
2269 if((s16)datasize < 2+1+(i+1)*6)
2270 throw con::InvalidIncomingDataException
2271 ("DELETEDBLOCKS length is too short");
2272 v3s16 p = readV3S16(&data[2+1+i*6]);
2273 /*infostream<<"Server: DELETEDBLOCKS ("
2274 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2275 RemoteClient *client = getClient(peer_id);
2276 client->SetBlockNotSent(p);
2279 else if(command == TOSERVER_CLICK_OBJECT)
2281 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2284 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2289 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2295 [2] u8 button (0=left, 1=right)
2299 u8 button = readU8(&data[2]);
2300 u16 id = readS16(&data[3]);
2301 u16 item_i = readU16(&data[5]);
2303 ServerActiveObject *obj = m_env->getActiveObject(id);
2307 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2312 // Skip if object has been removed
2316 //TODO: Check that object is reasonably close
2318 // Get ServerRemotePlayer
2319 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2321 // Update wielded item
2322 srp->wieldItem(item_i);
2324 // Left click, pick/punch
2327 actionstream<<player->getName()<<" punches object "
2328 <<obj->getId()<<std::endl;
2335 Try creating inventory item
2337 InventoryItem *item = obj->createPickedUpItem();
2341 InventoryList *ilist = player->inventory.getList("main");
2344 actionstream<<player->getName()<<" picked up "
2345 <<item->getName()<<std::endl;
2346 if(g_settings->getBool("creative_mode") == false)
2348 // Skip if inventory has no free space
2349 if(ilist->roomForItem(item) == false)
2351 infostream<<"Player inventory has no free space"<<std::endl;
2355 // Add to inventory and send inventory
2356 ilist->addItem(item);
2357 UpdateCrafting(player->peer_id);
2358 SendInventory(player->peer_id);
2361 // Remove object from environment
2362 obj->m_removed = true;
2368 Item cannot be picked up. Punch it instead.
2371 actionstream<<player->getName()<<" punches object "
2372 <<obj->getId()<<std::endl;
2374 ToolItem *titem = NULL;
2375 std::string toolname = "";
2377 InventoryList *mlist = player->inventory.getList("main");
2380 InventoryItem *item = mlist->getItem(item_i);
2381 if(item && (std::string)item->getName() == "ToolItem")
2383 titem = (ToolItem*)item;
2384 toolname = titem->getToolName();
2388 v3f playerpos = player->getPosition();
2389 v3f objpos = obj->getBasePosition();
2390 v3f dir = (objpos - playerpos).normalize();
2392 u16 wear = obj->punch(toolname, dir, player->getName());
2396 bool weared_out = titem->addWear(wear);
2398 mlist->deleteItem(item_i);
2399 SendInventory(player->peer_id);
2404 // Right click, do something with object
2407 actionstream<<player->getName()<<" right clicks object "
2408 <<obj->getId()<<std::endl;
2411 obj->rightClick(srp);
2415 Update player state to client
2417 SendPlayerHP(player);
2418 UpdateCrafting(player->peer_id);
2419 SendInventory(player->peer_id);
2421 else if(command == TOSERVER_GROUND_ACTION)
2429 [3] v3s16 nodepos_undersurface
2430 [9] v3s16 nodepos_abovesurface
2435 2: stop digging (all parameters ignored)
2436 3: digging completed
2438 u8 action = readU8(&data[2]);
2440 p_under.X = readS16(&data[3]);
2441 p_under.Y = readS16(&data[5]);
2442 p_under.Z = readS16(&data[7]);
2444 p_over.X = readS16(&data[9]);
2445 p_over.Y = readS16(&data[11]);
2446 p_over.Z = readS16(&data[13]);
2447 u16 item_i = readU16(&data[15]);
2449 //TODO: Check that target is reasonably close
2457 NOTE: This can be used in the future to check if
2458 somebody is cheating, by checking the timing.
2465 else if(action == 2)
2468 RemoteClient *client = getClient(peer_id);
2469 JMutexAutoLock digmutex(client->m_dig_mutex);
2470 client->m_dig_tool_item = -1;
2475 3: Digging completed
2477 else if(action == 3)
2479 // Mandatory parameter; actually used for nothing
2480 core::map<v3s16, MapBlock*> modified_blocks;
2482 content_t material = CONTENT_IGNORE;
2483 u8 mineral = MINERAL_NONE;
2485 bool cannot_remove_node = false;
2489 MapNode n = m_env->getMap().getNode(p_under);
2491 mineral = n.getMineral(m_nodemgr);
2492 // Get material at position
2493 material = n.getContent();
2494 // If not yet cancelled
2495 if(cannot_remove_node == false)
2497 // If it's not diggable, do nothing
2498 if(m_nodemgr->get(material).diggable == false)
2500 infostream<<"Server: Not finishing digging: "
2501 <<"Node not diggable"
2503 cannot_remove_node = true;
2506 // If not yet cancelled
2507 if(cannot_remove_node == false)
2509 // Get node metadata
2510 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2511 if(meta && meta->nodeRemovalDisabled() == true)
2513 infostream<<"Server: Not finishing digging: "
2514 <<"Node metadata disables removal"
2516 cannot_remove_node = true;
2520 catch(InvalidPositionException &e)
2522 infostream<<"Server: Not finishing digging: Node not found."
2523 <<" Adding block to emerge queue."
2525 m_emerge_queue.addBlock(peer_id,
2526 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2527 cannot_remove_node = true;
2530 // Make sure the player is allowed to do it
2531 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2533 infostream<<"Player "<<player->getName()<<" cannot remove node"
2534 <<" because privileges are "<<getPlayerPrivs(player)
2536 cannot_remove_node = true;
2540 If node can't be removed, set block to be re-sent to
2543 if(cannot_remove_node)
2545 infostream<<"Server: Not finishing digging."<<std::endl;
2547 // Client probably has wrong data.
2548 // Set block not sent, so that client will get
2550 infostream<<"Client "<<peer_id<<" tried to dig "
2551 <<"node; but node cannot be removed."
2552 <<" setting MapBlock not sent."<<std::endl;
2553 RemoteClient *client = getClient(peer_id);
2554 v3s16 blockpos = getNodeBlockPos(p_under);
2555 client->SetBlockNotSent(blockpos);
2560 actionstream<<player->getName()<<" digs "<<PP(p_under)
2561 <<", gets material "<<(int)material<<", mineral "
2562 <<(int)mineral<<std::endl;
2565 Send the removal to all close-by players.
2566 - If other player is close, send REMOVENODE
2567 - Otherwise set blocks not sent
2569 core::list<u16> far_players;
2570 sendRemoveNode(p_under, peer_id, &far_players, 30);
2573 Update and send inventory
2576 if(g_settings->getBool("creative_mode") == false)
2581 InventoryList *mlist = player->inventory.getList("main");
2584 InventoryItem *item = mlist->getItem(item_i);
2585 if(item && (std::string)item->getName() == "ToolItem")
2587 ToolItem *titem = (ToolItem*)item;
2588 std::string toolname = titem->getToolName();
2590 // Get digging properties for material and tool
2591 ToolDiggingProperties tp =
2592 m_toolmgr->getDiggingProperties(toolname);
2593 DiggingProperties prop =
2594 getDiggingProperties(material, &tp, m_nodemgr);
2596 if(prop.diggable == false)
2598 infostream<<"Server: WARNING: Player digged"
2599 <<" with impossible material + tool"
2600 <<" combination"<<std::endl;
2603 bool weared_out = titem->addWear(prop.wear);
2607 mlist->deleteItem(item_i);
2613 Add dug item to inventory
2616 InventoryItem *item = NULL;
2618 if(mineral != MINERAL_NONE)
2619 item = getDiggedMineralItem(mineral, this);
2624 const std::string &dug_s = m_nodemgr->get(material).dug_item;
2627 std::istringstream is(dug_s, std::ios::binary);
2628 item = InventoryItem::deSerialize(is, this);
2634 // Add a item to inventory
2635 player->inventory.addItem("main", item);
2638 UpdateCrafting(player->peer_id);
2639 SendInventory(player->peer_id);
2644 if(mineral != MINERAL_NONE)
2645 item = getDiggedMineralItem(mineral, this);
2650 const std::string &extra_dug_s = m_nodemgr->get(material).extra_dug_item;
2651 s32 extra_rarity = m_nodemgr->get(material).extra_dug_item_rarity;
2652 if(extra_dug_s != "" && extra_rarity != 0
2653 && myrand() % extra_rarity == 0)
2655 std::istringstream is(extra_dug_s, std::ios::binary);
2656 item = InventoryItem::deSerialize(is, this);
2662 // Add a item to inventory
2663 player->inventory.addItem("main", item);
2666 UpdateCrafting(player->peer_id);
2667 SendInventory(player->peer_id);
2673 (this takes some time so it is done after the quick stuff)
2676 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2678 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2681 Set blocks not sent to far players
2683 for(core::list<u16>::Iterator
2684 i = far_players.begin();
2685 i != far_players.end(); i++)
2688 RemoteClient *client = getClient(peer_id);
2691 client->SetBlocksNotSent(modified_blocks);
2698 else if(action == 1)
2701 InventoryList *ilist = player->inventory.getList("main");
2706 InventoryItem *item = ilist->getItem(item_i);
2708 // If there is no item, it is not possible to add it anywhere
2713 Handle material items
2715 if(std::string("MaterialItem") == item->getName())
2718 // Don't add a node if this is not a free space
2719 MapNode n2 = m_env->getMap().getNode(p_over);
2720 bool no_enough_privs =
2721 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2723 infostream<<"Player "<<player->getName()<<" cannot add node"
2724 <<" because privileges are "<<getPlayerPrivs(player)
2727 if(m_nodemgr->get(n2).buildable_to == false
2730 // Client probably has wrong data.
2731 // Set block not sent, so that client will get
2733 infostream<<"Client "<<peer_id<<" tried to place"
2734 <<" node in invalid position; setting"
2735 <<" MapBlock not sent."<<std::endl;
2736 RemoteClient *client = getClient(peer_id);
2737 v3s16 blockpos = getNodeBlockPos(p_over);
2738 client->SetBlockNotSent(blockpos);
2742 catch(InvalidPositionException &e)
2744 infostream<<"Server: Ignoring ADDNODE: Node not found"
2745 <<" Adding block to emerge queue."
2747 m_emerge_queue.addBlock(peer_id,
2748 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2752 // Reset build time counter
2753 getClient(peer_id)->m_time_from_building = 0.0;
2756 MaterialItem *mitem = (MaterialItem*)item;
2758 n.setContent(mitem->getMaterial());
2760 actionstream<<player->getName()<<" places material "
2761 <<(int)mitem->getMaterial()
2762 <<" at "<<PP(p_under)<<std::endl;
2764 // Calculate direction for wall mounted stuff
2765 if(m_nodemgr->get(n).wall_mounted)
2766 n.param2 = packDir(p_under - p_over);
2768 // Calculate the direction for furnaces and chests and stuff
2769 if(m_nodemgr->get(n).param_type == CPT_FACEDIR_SIMPLE)
2771 v3f playerpos = player->getPosition();
2772 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2773 blockpos = blockpos.normalize();
2775 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2789 Send to all close-by players
2791 core::list<u16> far_players;
2792 sendAddNode(p_over, n, 0, &far_players, 30);
2797 InventoryList *ilist = player->inventory.getList("main");
2798 if(g_settings->getBool("creative_mode") == false && ilist)
2800 // Remove from inventory and send inventory
2801 if(mitem->getCount() == 1)
2802 ilist->deleteItem(item_i);
2806 UpdateCrafting(peer_id);
2807 SendInventory(peer_id);
2813 This takes some time so it is done after the quick stuff
2815 core::map<v3s16, MapBlock*> modified_blocks;
2817 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2819 std::string p_name = std::string(player->getName());
2820 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2823 Set blocks not sent to far players
2825 for(core::list<u16>::Iterator
2826 i = far_players.begin();
2827 i != far_players.end(); i++)
2830 RemoteClient *client = getClient(peer_id);
2833 client->SetBlocksNotSent(modified_blocks);
2837 Calculate special events
2840 /*if(n.d == CONTENT_MESE)
2843 for(s16 z=-1; z<=1; z++)
2844 for(s16 y=-1; y<=1; y++)
2845 for(s16 x=-1; x<=1; x++)
2852 Place other item (not a block)
2856 v3s16 blockpos = getNodeBlockPos(p_over);
2859 Check that the block is loaded so that the item
2860 can properly be added to the static list too
2862 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2865 infostream<<"Error while placing object: "
2866 "block not found"<<std::endl;
2871 If in creative mode, item dropping is disabled unless
2872 player has build privileges
2874 if(g_settings->getBool("creative_mode") &&
2875 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2877 infostream<<"Not allowing player to drop item: "
2878 "creative mode and no build privs"<<std::endl;
2882 // Calculate a position for it
2883 v3f pos = intToFloat(p_over, BS);
2885 /*pos.Y -= BS*0.25; // let it drop a bit
2887 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2888 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2893 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2897 infostream<<"WARNING: item resulted in NULL object, "
2898 <<"not placing onto map"
2903 actionstream<<player->getName()<<" places "<<item->getName()
2904 <<" at "<<PP(p_over)<<std::endl;
2906 // Add the object to the environment
2907 m_env->addActiveObject(obj);
2909 infostream<<"Placed object"<<std::endl;
2911 if(g_settings->getBool("creative_mode") == false)
2913 // Delete the right amount of items from the slot
2914 u16 dropcount = item->getDropCount();
2916 // Delete item if all gone
2917 if(item->getCount() <= dropcount)
2919 if(item->getCount() < dropcount)
2920 infostream<<"WARNING: Server: dropped more items"
2921 <<" than the slot contains"<<std::endl;
2923 InventoryList *ilist = player->inventory.getList("main");
2925 // Remove from inventory and send inventory
2926 ilist->deleteItem(item_i);
2928 // Else decrement it
2930 item->remove(dropcount);
2933 UpdateCrafting(peer_id);
2934 SendInventory(peer_id);
2942 Catch invalid actions
2946 infostream<<"WARNING: Server: Invalid action "
2947 <<action<<std::endl;
2951 else if(command == TOSERVER_RELEASE)
2960 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2963 else if(command == TOSERVER_SIGNTEXT)
2965 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2969 else if(command == TOSERVER_SIGNNODETEXT)
2971 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2979 std::string datastring((char*)&data[2], datasize-2);
2980 std::istringstream is(datastring, std::ios_base::binary);
2983 is.read((char*)buf, 6);
2984 v3s16 p = readV3S16(buf);
2985 is.read((char*)buf, 2);
2986 u16 textlen = readU16(buf);
2988 for(u16 i=0; i<textlen; i++)
2990 is.read((char*)buf, 1);
2991 text += (char)buf[0];
2994 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2997 if(meta->typeId() != CONTENT_SIGN_WALL)
2999 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3000 signmeta->setText(text);
3002 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3003 <<" at "<<PP(p)<<std::endl;
3005 v3s16 blockpos = getNodeBlockPos(p);
3006 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3009 block->setChangedFlag();
3012 for(core::map<u16, RemoteClient*>::Iterator
3013 i = m_clients.getIterator();
3014 i.atEnd()==false; i++)
3016 RemoteClient *client = i.getNode()->getValue();
3017 client->SetBlockNotSent(blockpos);
3020 else if(command == TOSERVER_INVENTORY_ACTION)
3022 /*// Ignore inventory changes if in creative mode
3023 if(g_settings->getBool("creative_mode") == true)
3025 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3029 // Strip command and create a stream
3030 std::string datastring((char*)&data[2], datasize-2);
3031 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3032 std::istringstream is(datastring, std::ios_base::binary);
3034 InventoryAction *a = InventoryAction::deSerialize(is);
3039 c.current_player = player;
3042 Handle craftresult specially if not in creative mode
3044 bool disable_action = false;
3045 if(a->getType() == IACTION_MOVE
3046 && g_settings->getBool("creative_mode") == false)
3048 IMoveAction *ma = (IMoveAction*)a;
3049 if(ma->to_inv == "current_player" &&
3050 ma->from_inv == "current_player")
3052 InventoryList *rlist = player->inventory.getList("craftresult");
3054 InventoryList *clist = player->inventory.getList("craft");
3056 InventoryList *mlist = player->inventory.getList("main");
3059 Craftresult is no longer preview if something
3062 if(ma->to_list == "craftresult"
3063 && ma->from_list != "craftresult")
3065 // If it currently is a preview, remove
3067 if(player->craftresult_is_preview)
3069 rlist->deleteItem(0);
3071 player->craftresult_is_preview = false;
3074 Crafting takes place if this condition is true.
3076 if(player->craftresult_is_preview &&
3077 ma->from_list == "craftresult")
3079 player->craftresult_is_preview = false;
3080 clist->decrementMaterials(1);
3082 /* Print out action */
3083 InventoryList *list =
3084 player->inventory.getList("craftresult");
3086 InventoryItem *item = list->getItem(0);
3087 std::string itemname = "NULL";
3089 itemname = item->getName();
3090 actionstream<<player->getName()<<" crafts "
3091 <<itemname<<std::endl;
3094 If the craftresult is placed on itself, move it to
3095 main inventory instead of doing the action
3097 if(ma->to_list == "craftresult"
3098 && ma->from_list == "craftresult")
3100 disable_action = true;
3102 InventoryItem *item1 = rlist->changeItem(0, NULL);
3103 mlist->addItem(item1);
3106 // Disallow moving items if not allowed to build
3107 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3111 // if it's a locking chest, only allow the owner or server admins to move items
3112 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3114 Strfnd fn(ma->from_inv);
3115 std::string id0 = fn.next(":");
3116 if(id0 == "nodemeta")
3119 p.X = stoi(fn.next(","));
3120 p.Y = stoi(fn.next(","));
3121 p.Z = stoi(fn.next(","));
3122 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3123 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3124 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3125 if (lcm->getOwner() != player->getName())
3130 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3132 Strfnd fn(ma->to_inv);
3133 std::string id0 = fn.next(":");
3134 if(id0 == "nodemeta")
3137 p.X = stoi(fn.next(","));
3138 p.Y = stoi(fn.next(","));
3139 p.Z = stoi(fn.next(","));
3140 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3141 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3142 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3143 if (lcm->getOwner() != player->getName())
3150 if(disable_action == false)
3152 // Feed action to player inventory
3160 UpdateCrafting(player->peer_id);
3161 SendInventory(player->peer_id);
3166 infostream<<"TOSERVER_INVENTORY_ACTION: "
3167 <<"InventoryAction::deSerialize() returned NULL"
3171 else if(command == TOSERVER_CHAT_MESSAGE)
3179 std::string datastring((char*)&data[2], datasize-2);
3180 std::istringstream is(datastring, std::ios_base::binary);
3183 is.read((char*)buf, 2);
3184 u16 len = readU16(buf);
3186 std::wstring message;
3187 for(u16 i=0; i<len; i++)
3189 is.read((char*)buf, 2);
3190 message += (wchar_t)readU16(buf);
3193 // Get player name of this client
3194 std::wstring name = narrow_to_wide(player->getName());
3196 // Line to send to players
3198 // Whether to send to the player that sent the line
3199 bool send_to_sender = false;
3200 // Whether to send to other players
3201 bool send_to_others = false;
3203 // Local player gets all privileges regardless of
3204 // what's set on their account.
3205 u64 privs = getPlayerPrivs(player);
3208 if(message[0] == L'/')
3210 size_t strip_size = 1;
3211 if (message[1] == L'#') // support old-style commans
3213 message = message.substr(strip_size);
3215 WStrfnd f1(message);
3216 f1.next(L" "); // Skip over /#whatever
3217 std::wstring paramstring = f1.next(L"");
3219 ServerCommandContext *ctx = new ServerCommandContext(
3220 str_split(message, L' '),
3227 std::wstring reply(processServerCommand(ctx));
3228 send_to_sender = ctx->flags & SEND_TO_SENDER;
3229 send_to_others = ctx->flags & SEND_TO_OTHERS;
3231 if (ctx->flags & SEND_NO_PREFIX)
3234 line += L"Server: " + reply;
3241 if(privs & PRIV_SHOUT)
3247 send_to_others = true;
3251 line += L"Server: You are not allowed to shout";
3252 send_to_sender = true;
3259 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3262 Send the message to clients
3264 for(core::map<u16, RemoteClient*>::Iterator
3265 i = m_clients.getIterator();
3266 i.atEnd() == false; i++)
3268 // Get client and check that it is valid
3269 RemoteClient *client = i.getNode()->getValue();
3270 assert(client->peer_id == i.getNode()->getKey());
3271 if(client->serialization_version == SER_FMT_VER_INVALID)
3275 bool sender_selected = (peer_id == client->peer_id);
3276 if(sender_selected == true && send_to_sender == false)
3278 if(sender_selected == false && send_to_others == false)
3281 SendChatMessage(client->peer_id, line);
3285 else if(command == TOSERVER_DAMAGE)
3287 std::string datastring((char*)&data[2], datasize-2);
3288 std::istringstream is(datastring, std::ios_base::binary);
3289 u8 damage = readU8(is);
3291 if(g_settings->getBool("enable_damage"))
3293 actionstream<<player->getName()<<" damaged by "
3294 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3297 HandlePlayerHP(player, damage);
3301 SendPlayerHP(player);
3304 else if(command == TOSERVER_PASSWORD)
3307 [0] u16 TOSERVER_PASSWORD
3308 [2] u8[28] old password
3309 [30] u8[28] new password
3312 if(datasize != 2+PASSWORD_SIZE*2)
3314 /*char password[PASSWORD_SIZE];
3315 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3316 password[i] = data[2+i];
3317 password[PASSWORD_SIZE-1] = 0;*/
3319 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3327 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3329 char c = data[2+PASSWORD_SIZE+i];
3335 infostream<<"Server: Client requests a password change from "
3336 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3338 std::string playername = player->getName();
3340 if(m_authmanager.exists(playername) == false)
3342 infostream<<"Server: playername not found in authmanager"<<std::endl;
3343 // Wrong old password supplied!!
3344 SendChatMessage(peer_id, L"playername not found in authmanager");
3348 std::string checkpwd = m_authmanager.getPassword(playername);
3350 if(oldpwd != checkpwd)
3352 infostream<<"Server: invalid old password"<<std::endl;
3353 // Wrong old password supplied!!
3354 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3358 actionstream<<player->getName()<<" changes password"<<std::endl;
3360 m_authmanager.setPassword(playername, newpwd);
3362 infostream<<"Server: password change successful for "<<playername
3364 SendChatMessage(peer_id, L"Password change successful");
3366 else if(command == TOSERVER_PLAYERITEM)
3371 u16 item = readU16(&data[2]);
3372 player->wieldItem(item);
3373 SendWieldedItem(player);
3375 else if(command == TOSERVER_RESPAWN)
3380 RespawnPlayer(player);
3382 actionstream<<player->getName()<<" respawns at "
3383 <<PP(player->getPosition()/BS)<<std::endl;
3387 infostream<<"Server::ProcessData(): Ignoring "
3388 "unknown command "<<command<<std::endl;
3392 catch(SendFailedException &e)
3394 errorstream<<"Server::ProcessData(): SendFailedException: "
3400 void Server::onMapEditEvent(MapEditEvent *event)
3402 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3403 if(m_ignore_map_edit_events)
3405 MapEditEvent *e = event->clone();
3406 m_unsent_map_edit_queue.push_back(e);
3409 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3411 if(id == "current_player")
3413 assert(c->current_player);
3414 return &(c->current_player->inventory);
3418 std::string id0 = fn.next(":");
3420 if(id0 == "nodemeta")
3423 p.X = stoi(fn.next(","));
3424 p.Y = stoi(fn.next(","));
3425 p.Z = stoi(fn.next(","));
3426 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3428 return meta->getInventory();
3429 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3430 <<"no metadata found"<<std::endl;
3434 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3437 void Server::inventoryModified(InventoryContext *c, std::string id)
3439 if(id == "current_player")
3441 assert(c->current_player);
3443 UpdateCrafting(c->current_player->peer_id);
3444 SendInventory(c->current_player->peer_id);
3449 std::string id0 = fn.next(":");
3451 if(id0 == "nodemeta")
3454 p.X = stoi(fn.next(","));
3455 p.Y = stoi(fn.next(","));
3456 p.Z = stoi(fn.next(","));
3457 v3s16 blockpos = getNodeBlockPos(p);
3459 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3461 meta->inventoryModified();
3463 for(core::map<u16, RemoteClient*>::Iterator
3464 i = m_clients.getIterator();
3465 i.atEnd()==false; i++)
3467 RemoteClient *client = i.getNode()->getValue();
3468 client->SetBlockNotSent(blockpos);
3474 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3477 core::list<PlayerInfo> Server::getPlayerInfo()
3479 DSTACK(__FUNCTION_NAME);
3480 JMutexAutoLock envlock(m_env_mutex);
3481 JMutexAutoLock conlock(m_con_mutex);
3483 core::list<PlayerInfo> list;
3485 core::list<Player*> players = m_env->getPlayers();
3487 core::list<Player*>::Iterator i;
3488 for(i = players.begin();
3489 i != players.end(); i++)
3493 Player *player = *i;
3496 // Copy info from connection to info struct
3497 info.id = player->peer_id;
3498 info.address = m_con.GetPeerAddress(player->peer_id);
3499 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3501 catch(con::PeerNotFoundException &e)
3503 // Set dummy peer info
3505 info.address = Address(0,0,0,0,0);
3509 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3510 info.position = player->getPosition();
3512 list.push_back(info);
3519 void Server::peerAdded(con::Peer *peer)
3521 DSTACK(__FUNCTION_NAME);
3522 infostream<<"Server::peerAdded(): peer->id="
3523 <<peer->id<<std::endl;
3526 c.type = PEER_ADDED;
3527 c.peer_id = peer->id;
3529 m_peer_change_queue.push_back(c);
3532 void Server::deletingPeer(con::Peer *peer, bool timeout)
3534 DSTACK(__FUNCTION_NAME);
3535 infostream<<"Server::deletingPeer(): peer->id="
3536 <<peer->id<<", timeout="<<timeout<<std::endl;
3539 c.type = PEER_REMOVED;
3540 c.peer_id = peer->id;
3541 c.timeout = timeout;
3542 m_peer_change_queue.push_back(c);
3549 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3551 DSTACK(__FUNCTION_NAME);
3552 std::ostringstream os(std::ios_base::binary);
3554 writeU16(os, TOCLIENT_HP);
3558 std::string s = os.str();
3559 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3561 con.Send(peer_id, 0, data, true);
3564 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3565 const std::wstring &reason)
3567 DSTACK(__FUNCTION_NAME);
3568 std::ostringstream os(std::ios_base::binary);
3570 writeU16(os, TOCLIENT_ACCESS_DENIED);
3571 os<<serializeWideString(reason);
3574 std::string s = os.str();
3575 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3577 con.Send(peer_id, 0, data, true);
3580 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3581 bool set_camera_point_target, v3f camera_point_target)
3583 DSTACK(__FUNCTION_NAME);
3584 std::ostringstream os(std::ios_base::binary);
3586 writeU16(os, TOCLIENT_DEATHSCREEN);
3587 writeU8(os, set_camera_point_target);
3588 writeV3F1000(os, camera_point_target);
3591 std::string s = os.str();
3592 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3594 con.Send(peer_id, 0, data, true);
3598 Non-static send methods
3601 void Server::SendObjectData(float dtime)
3603 DSTACK(__FUNCTION_NAME);
3605 core::map<v3s16, bool> stepped_blocks;
3607 for(core::map<u16, RemoteClient*>::Iterator
3608 i = m_clients.getIterator();
3609 i.atEnd() == false; i++)
3611 u16 peer_id = i.getNode()->getKey();
3612 RemoteClient *client = i.getNode()->getValue();
3613 assert(client->peer_id == peer_id);
3615 if(client->serialization_version == SER_FMT_VER_INVALID)
3618 client->SendObjectData(this, dtime, stepped_blocks);
3622 void Server::SendPlayerInfos()
3624 DSTACK(__FUNCTION_NAME);
3626 //JMutexAutoLock envlock(m_env_mutex);
3628 // Get connected players
3629 core::list<Player*> players = m_env->getPlayers(true);
3631 u32 player_count = players.getSize();
3632 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3634 SharedBuffer<u8> data(datasize);
3635 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3638 core::list<Player*>::Iterator i;
3639 for(i = players.begin();
3640 i != players.end(); i++)
3642 Player *player = *i;
3644 /*infostream<<"Server sending player info for player with "
3645 "peer_id="<<player->peer_id<<std::endl;*/
3647 writeU16(&data[start], player->peer_id);
3648 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3649 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3650 start += 2+PLAYERNAME_SIZE;
3653 //JMutexAutoLock conlock(m_con_mutex);
3656 m_con.SendToAll(0, data, true);
3659 void Server::SendInventory(u16 peer_id)
3661 DSTACK(__FUNCTION_NAME);
3663 Player* player = m_env->getPlayer(peer_id);
3670 std::ostringstream os;
3671 //os.imbue(std::locale("C"));
3673 player->inventory.serialize(os);
3675 std::string s = os.str();
3677 SharedBuffer<u8> data(s.size()+2);
3678 writeU16(&data[0], TOCLIENT_INVENTORY);
3679 memcpy(&data[2], s.c_str(), s.size());
3682 m_con.Send(peer_id, 0, data, true);
3685 std::string getWieldedItemString(const Player *player)
3687 const InventoryItem *item = player->getWieldItem();
3689 return std::string("");
3690 std::ostringstream os(std::ios_base::binary);
3691 item->serialize(os);
3695 void Server::SendWieldedItem(const Player* player)
3697 DSTACK(__FUNCTION_NAME);
3701 std::ostringstream os(std::ios_base::binary);
3703 writeU16(os, TOCLIENT_PLAYERITEM);
3705 writeU16(os, player->peer_id);
3706 os<<serializeString(getWieldedItemString(player));
3709 std::string s = os.str();
3710 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3712 m_con.SendToAll(0, data, true);
3715 void Server::SendPlayerItems()
3717 DSTACK(__FUNCTION_NAME);
3719 std::ostringstream os(std::ios_base::binary);
3720 core::list<Player *> players = m_env->getPlayers(true);
3722 writeU16(os, TOCLIENT_PLAYERITEM);
3723 writeU16(os, players.size());
3724 core::list<Player *>::Iterator i;
3725 for(i = players.begin(); i != players.end(); ++i)
3728 writeU16(os, p->peer_id);
3729 os<<serializeString(getWieldedItemString(p));
3733 std::string s = os.str();
3734 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3736 m_con.SendToAll(0, data, true);
3739 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3741 DSTACK(__FUNCTION_NAME);
3743 std::ostringstream os(std::ios_base::binary);
3747 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3748 os.write((char*)buf, 2);
3751 writeU16(buf, message.size());
3752 os.write((char*)buf, 2);
3755 for(u32 i=0; i<message.size(); i++)
3759 os.write((char*)buf, 2);
3763 std::string s = os.str();
3764 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3766 m_con.Send(peer_id, 0, data, true);
3769 void Server::BroadcastChatMessage(const std::wstring &message)
3771 for(core::map<u16, RemoteClient*>::Iterator
3772 i = m_clients.getIterator();
3773 i.atEnd() == false; i++)
3775 // Get client and check that it is valid
3776 RemoteClient *client = i.getNode()->getValue();
3777 assert(client->peer_id == i.getNode()->getKey());
3778 if(client->serialization_version == SER_FMT_VER_INVALID)
3781 SendChatMessage(client->peer_id, message);
3785 void Server::SendPlayerHP(Player *player)
3787 SendHP(m_con, player->peer_id, player->hp);
3790 void Server::SendMovePlayer(Player *player)
3792 DSTACK(__FUNCTION_NAME);
3793 std::ostringstream os(std::ios_base::binary);
3795 writeU16(os, TOCLIENT_MOVE_PLAYER);
3796 writeV3F1000(os, player->getPosition());
3797 writeF1000(os, player->getPitch());
3798 writeF1000(os, player->getYaw());
3801 v3f pos = player->getPosition();
3802 f32 pitch = player->getPitch();
3803 f32 yaw = player->getYaw();
3804 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3805 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3812 std::string s = os.str();
3813 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3815 m_con.Send(player->peer_id, 0, data, true);
3818 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3819 core::list<u16> *far_players, float far_d_nodes)
3821 float maxd = far_d_nodes*BS;
3822 v3f p_f = intToFloat(p, BS);
3826 SharedBuffer<u8> reply(replysize);
3827 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3828 writeS16(&reply[2], p.X);
3829 writeS16(&reply[4], p.Y);
3830 writeS16(&reply[6], p.Z);
3832 for(core::map<u16, RemoteClient*>::Iterator
3833 i = m_clients.getIterator();
3834 i.atEnd() == false; i++)
3836 // Get client and check that it is valid
3837 RemoteClient *client = i.getNode()->getValue();
3838 assert(client->peer_id == i.getNode()->getKey());
3839 if(client->serialization_version == SER_FMT_VER_INVALID)
3842 // Don't send if it's the same one
3843 if(client->peer_id == ignore_id)
3849 Player *player = m_env->getPlayer(client->peer_id);
3852 // If player is far away, only set modified blocks not sent
3853 v3f player_pos = player->getPosition();
3854 if(player_pos.getDistanceFrom(p_f) > maxd)
3856 far_players->push_back(client->peer_id);
3863 m_con.Send(client->peer_id, 0, reply, true);
3867 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3868 core::list<u16> *far_players, float far_d_nodes)
3870 float maxd = far_d_nodes*BS;
3871 v3f p_f = intToFloat(p, BS);
3873 for(core::map<u16, RemoteClient*>::Iterator
3874 i = m_clients.getIterator();
3875 i.atEnd() == false; i++)
3877 // Get client and check that it is valid
3878 RemoteClient *client = i.getNode()->getValue();
3879 assert(client->peer_id == i.getNode()->getKey());
3880 if(client->serialization_version == SER_FMT_VER_INVALID)
3883 // Don't send if it's the same one
3884 if(client->peer_id == ignore_id)
3890 Player *player = m_env->getPlayer(client->peer_id);
3893 // If player is far away, only set modified blocks not sent
3894 v3f player_pos = player->getPosition();
3895 if(player_pos.getDistanceFrom(p_f) > maxd)
3897 far_players->push_back(client->peer_id);
3904 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3905 SharedBuffer<u8> reply(replysize);
3906 writeU16(&reply[0], TOCLIENT_ADDNODE);
3907 writeS16(&reply[2], p.X);
3908 writeS16(&reply[4], p.Y);
3909 writeS16(&reply[6], p.Z);
3910 n.serialize(&reply[8], client->serialization_version);
3913 m_con.Send(client->peer_id, 0, reply, true);
3917 void Server::setBlockNotSent(v3s16 p)
3919 for(core::map<u16, RemoteClient*>::Iterator
3920 i = m_clients.getIterator();
3921 i.atEnd()==false; i++)
3923 RemoteClient *client = i.getNode()->getValue();
3924 client->SetBlockNotSent(p);
3928 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3930 DSTACK(__FUNCTION_NAME);
3932 v3s16 p = block->getPos();
3936 bool completely_air = true;
3937 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3938 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3939 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3941 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3943 completely_air = false;
3944 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3949 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3951 infostream<<"[completely air] ";
3952 infostream<<std::endl;
3956 Create a packet with the block in the right format
3959 std::ostringstream os(std::ios_base::binary);
3960 block->serialize(os, ver);
3961 std::string s = os.str();
3962 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3964 u32 replysize = 8 + blockdata.getSize();
3965 SharedBuffer<u8> reply(replysize);
3966 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3967 writeS16(&reply[2], p.X);
3968 writeS16(&reply[4], p.Y);
3969 writeS16(&reply[6], p.Z);
3970 memcpy(&reply[8], *blockdata, blockdata.getSize());
3972 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3973 <<": \tpacket size: "<<replysize<<std::endl;*/
3978 m_con.Send(peer_id, 1, reply, true);
3981 void Server::SendBlocks(float dtime)
3983 DSTACK(__FUNCTION_NAME);
3985 JMutexAutoLock envlock(m_env_mutex);
3986 JMutexAutoLock conlock(m_con_mutex);
3988 //TimeTaker timer("Server::SendBlocks");
3990 core::array<PrioritySortedBlockTransfer> queue;
3992 s32 total_sending = 0;
3995 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3997 for(core::map<u16, RemoteClient*>::Iterator
3998 i = m_clients.getIterator();
3999 i.atEnd() == false; i++)
4001 RemoteClient *client = i.getNode()->getValue();
4002 assert(client->peer_id == i.getNode()->getKey());
4004 total_sending += client->SendingCount();
4006 if(client->serialization_version == SER_FMT_VER_INVALID)
4009 client->GetNextBlocks(this, dtime, queue);
4014 // Lowest priority number comes first.
4015 // Lowest is most important.
4018 for(u32 i=0; i<queue.size(); i++)
4020 //TODO: Calculate limit dynamically
4021 if(total_sending >= g_settings->getS32
4022 ("max_simultaneous_block_sends_server_total"))
4025 PrioritySortedBlockTransfer q = queue[i];
4027 MapBlock *block = NULL;
4030 block = m_env->getMap().getBlockNoCreate(q.pos);
4032 catch(InvalidPositionException &e)
4037 RemoteClient *client = getClient(q.peer_id);
4039 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4041 client->SentBlock(q.pos);
4051 void Server::HandlePlayerHP(Player *player, s16 damage)
4053 if(player->hp > damage)
4055 player->hp -= damage;
4056 SendPlayerHP(player);
4060 infostream<<"Server::HandlePlayerHP(): Player "
4061 <<player->getName()<<" dies"<<std::endl;
4065 //TODO: Throw items around
4067 // Handle players that are not connected
4068 if(player->peer_id == PEER_ID_INEXISTENT){
4069 RespawnPlayer(player);
4073 SendPlayerHP(player);
4075 RemoteClient *client = getClient(player->peer_id);
4076 if(client->net_proto_version >= 3)
4078 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4082 RespawnPlayer(player);
4087 void Server::RespawnPlayer(Player *player)
4089 v3f pos = findSpawnPos(m_env->getServerMap());
4090 player->setPosition(pos);
4092 SendMovePlayer(player);
4093 SendPlayerHP(player);
4096 void Server::UpdateCrafting(u16 peer_id)
4098 DSTACK(__FUNCTION_NAME);
4100 Player* player = m_env->getPlayer(peer_id);
4104 Calculate crafting stuff
4106 if(g_settings->getBool("creative_mode") == false)
4108 InventoryList *clist = player->inventory.getList("craft");
4109 InventoryList *rlist = player->inventory.getList("craftresult");
4111 if(rlist && rlist->getUsedSlots() == 0)
4112 player->craftresult_is_preview = true;
4114 if(rlist && player->craftresult_is_preview)
4116 rlist->clearItems();
4118 if(clist && rlist && player->craftresult_is_preview)
4120 InventoryItem *items[9];
4121 for(u16 i=0; i<9; i++)
4123 items[i] = clist->getItem(i);
4126 // Get result of crafting grid
4127 InventoryItem *result = craft_get_result(items, this);
4129 rlist->addItem(result);
4132 } // if creative_mode == false
4135 RemoteClient* Server::getClient(u16 peer_id)
4137 DSTACK(__FUNCTION_NAME);
4138 //JMutexAutoLock lock(m_con_mutex);
4139 core::map<u16, RemoteClient*>::Node *n;
4140 n = m_clients.find(peer_id);
4141 // A client should exist for all peers
4143 return n->getValue();
4146 std::wstring Server::getStatusString()
4148 std::wostringstream os(std::ios_base::binary);
4151 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4153 os<<L", uptime="<<m_uptime.get();
4154 // Information about clients
4156 for(core::map<u16, RemoteClient*>::Iterator
4157 i = m_clients.getIterator();
4158 i.atEnd() == false; i++)
4160 // Get client and check that it is valid
4161 RemoteClient *client = i.getNode()->getValue();
4162 assert(client->peer_id == i.getNode()->getKey());
4163 if(client->serialization_version == SER_FMT_VER_INVALID)
4166 Player *player = m_env->getPlayer(client->peer_id);
4167 // Get name of player
4168 std::wstring name = L"unknown";
4170 name = narrow_to_wide(player->getName());
4171 // Add name to information string
4175 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4176 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4177 if(g_settings->get("motd") != "")
4178 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4182 // Saves g_settings to configpath given at initialization
4183 void Server::saveConfig()
4185 if(m_configpath != "")
4186 g_settings->updateConfigFile(m_configpath.c_str());
4189 void Server::notifyPlayer(const char *name, const std::wstring msg)
4191 Player *player = m_env->getPlayer(name);
4194 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4197 void Server::notifyPlayers(const std::wstring msg)
4199 BroadcastChatMessage(msg);
4202 // IGameDef interface
4204 IToolDefManager* Server::getToolDefManager()
4208 INodeDefManager* Server::getNodeDefManager()
4212 ITextureSource* Server::getTextureSource()
4217 v3f findSpawnPos(ServerMap &map)
4219 //return v3f(50,50,50)*BS;
4224 nodepos = v2s16(0,0);
4229 // Try to find a good place a few times
4230 for(s32 i=0; i<1000; i++)
4233 // We're going to try to throw the player to this position
4234 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4235 -range + (myrand()%(range*2)));
4236 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4237 // Get ground height at point (fallbacks to heightmap function)
4238 s16 groundheight = map.findGroundLevel(nodepos2d);
4239 // Don't go underwater
4240 if(groundheight < WATER_LEVEL)
4242 //infostream<<"-> Underwater"<<std::endl;
4245 // Don't go to high places
4246 if(groundheight > WATER_LEVEL + 4)
4248 //infostream<<"-> Underwater"<<std::endl;
4252 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4253 bool is_good = false;
4255 for(s32 i=0; i<10; i++){
4256 v3s16 blockpos = getNodeBlockPos(nodepos);
4257 map.emergeBlock(blockpos, true);
4258 MapNode n = map.getNodeNoEx(nodepos);
4259 if(n.getContent() == CONTENT_AIR){
4270 // Found a good place
4271 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4277 return intToFloat(nodepos, BS);
4280 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4283 Try to get an existing player
4285 Player *player = m_env->getPlayer(name);
4288 // If player is already connected, cancel
4289 if(player->peer_id != 0)
4291 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4296 player->peer_id = peer_id;
4298 // Reset inventory to creative if in creative mode
4299 if(g_settings->getBool("creative_mode"))
4301 // Warning: double code below
4302 // Backup actual inventory
4303 player->inventory_backup = new Inventory();
4304 *(player->inventory_backup) = player->inventory;
4305 // Set creative inventory
4306 craft_set_creative_inventory(player, this);
4313 If player with the wanted peer_id already exists, cancel.
4315 if(m_env->getPlayer(peer_id) != NULL)
4317 infostream<<"emergePlayer(): Player with wrong name but same"
4318 " peer_id already exists"<<std::endl;
4326 // Add authentication stuff
4327 m_authmanager.add(name);
4328 m_authmanager.setPassword(name, password);
4329 m_authmanager.setPrivs(name,
4330 stringToPrivs(g_settings->get("default_privs")));
4336 infostream<<"Server: Finding spawn place for player \""
4337 <<name<<"\""<<std::endl;
4339 v3f pos = findSpawnPos(m_env->getServerMap());
4341 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4344 Add player to environment
4347 m_env->addPlayer(player);
4350 Add stuff to inventory
4353 if(g_settings->getBool("creative_mode"))
4355 // Warning: double code above
4356 // Backup actual inventory
4357 player->inventory_backup = new Inventory();
4358 *(player->inventory_backup) = player->inventory;
4359 // Set creative inventory
4360 craft_set_creative_inventory(player, this);
4362 else if(g_settings->getBool("give_initial_stuff"))
4364 craft_give_initial_stuff(player, this);
4369 } // create new player
4372 void Server::handlePeerChange(PeerChange &c)
4374 JMutexAutoLock envlock(m_env_mutex);
4375 JMutexAutoLock conlock(m_con_mutex);
4377 if(c.type == PEER_ADDED)
4384 core::map<u16, RemoteClient*>::Node *n;
4385 n = m_clients.find(c.peer_id);
4386 // The client shouldn't already exist
4390 RemoteClient *client = new RemoteClient();
4391 client->peer_id = c.peer_id;
4392 m_clients.insert(client->peer_id, client);
4395 else if(c.type == PEER_REMOVED)
4402 core::map<u16, RemoteClient*>::Node *n;
4403 n = m_clients.find(c.peer_id);
4404 // The client should exist
4408 Mark objects to be not known by the client
4410 RemoteClient *client = n->getValue();
4412 for(core::map<u16, bool>::Iterator
4413 i = client->m_known_objects.getIterator();
4414 i.atEnd()==false; i++)
4417 u16 id = i.getNode()->getKey();
4418 ServerActiveObject* obj = m_env->getActiveObject(id);
4420 if(obj && obj->m_known_by_count > 0)
4421 obj->m_known_by_count--;
4424 // Collect information about leaving in chat
4425 std::wstring message;
4427 Player *player = m_env->getPlayer(c.peer_id);
4430 std::wstring name = narrow_to_wide(player->getName());
4433 message += L" left game";
4435 message += L" (timed out)";
4441 m_env->removePlayer(c.peer_id);
4444 // Set player client disconnected
4446 Player *player = m_env->getPlayer(c.peer_id);
4448 player->peer_id = 0;
4455 std::ostringstream os(std::ios_base::binary);
4456 for(core::map<u16, RemoteClient*>::Iterator
4457 i = m_clients.getIterator();
4458 i.atEnd() == false; i++)
4460 RemoteClient *client = i.getNode()->getValue();
4461 assert(client->peer_id == i.getNode()->getKey());
4462 if(client->serialization_version == SER_FMT_VER_INVALID)
4465 Player *player = m_env->getPlayer(client->peer_id);
4468 // Get name of player
4469 os<<player->getName()<<" ";
4472 actionstream<<player->getName()<<" "
4473 <<(c.timeout?"times out.":"leaves game.")
4474 <<" List of players: "
4475 <<os.str()<<std::endl;
4480 delete m_clients[c.peer_id];
4481 m_clients.remove(c.peer_id);
4483 // Send player info to all remaining clients
4486 // Send leave chat message to all remaining clients
4487 BroadcastChatMessage(message);
4496 void Server::handlePeerChanges()
4498 while(m_peer_change_queue.size() > 0)
4500 PeerChange c = m_peer_change_queue.pop_front();
4502 infostream<<"Server: Handling peer change: "
4503 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4506 handlePeerChange(c);
4510 u64 Server::getPlayerPrivs(Player *player)
4514 std::string playername = player->getName();
4515 // Local player gets all privileges regardless of
4516 // what's set on their account.
4517 if(g_settings->get("name") == playername)
4523 return getPlayerAuthPrivs(playername);
4527 void dedicated_server_loop(Server &server, bool &kill)
4529 DSTACK(__FUNCTION_NAME);
4531 infostream<<DTIME<<std::endl;
4532 infostream<<"========================"<<std::endl;
4533 infostream<<"Running dedicated server"<<std::endl;
4534 infostream<<"========================"<<std::endl;
4535 infostream<<std::endl;
4537 IntervalLimiter m_profiler_interval;
4541 // This is kind of a hack but can be done like this
4542 // because server.step() is very light
4544 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4549 if(server.getShutdownRequested() || kill)
4551 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4558 float profiler_print_interval =
4559 g_settings->getFloat("profiler_print_interval");
4560 if(profiler_print_interval != 0)
4562 if(m_profiler_interval.step(0.030, profiler_print_interval))
4564 infostream<<"Profiler:"<<std::endl;
4565 g_profiler->print(infostream);
4566 g_profiler->clear();
4573 static int counter = 0;
4579 core::list<PlayerInfo> list = server.getPlayerInfo();
4580 core::list<PlayerInfo>::Iterator i;
4581 static u32 sum_old = 0;
4582 u32 sum = PIChecksum(list);
4585 infostream<<DTIME<<"Player info:"<<std::endl;
4586 for(i=list.begin(); i!=list.end(); i++)
4588 i->PrintLine(&infostream);