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"
44 #include "mapnode_contentfeatures.h"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
50 class MapEditEventIgnorer
53 MapEditEventIgnorer(bool *flag):
62 ~MapEditEventIgnorer()
75 void * ServerThread::Thread()
79 log_register_thread("ServerThread");
81 DSTACK(__FUNCTION_NAME);
83 BEGIN_DEBUG_EXCEPTION_HANDLER
88 //TimeTaker timer("AsyncRunStep() + Receive()");
91 //TimeTaker timer("AsyncRunStep()");
92 m_server->AsyncRunStep();
95 //infostream<<"Running m_server->Receive()"<<std::endl;
98 catch(con::NoIncomingDataException &e)
101 catch(con::PeerNotFoundException &e)
103 infostream<<"Server: PeerNotFoundException"<<std::endl;
107 END_DEBUG_EXCEPTION_HANDLER(errorstream)
112 void * EmergeThread::Thread()
116 log_register_thread("EmergeThread");
118 DSTACK(__FUNCTION_NAME);
120 BEGIN_DEBUG_EXCEPTION_HANDLER
122 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
125 Get block info from queue, emerge them and send them
128 After queue is empty, exit.
132 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
136 SharedPtr<QueuedBlockEmerge> q(qptr);
142 Do not generate over-limit
144 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
145 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
146 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
152 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
154 //TimeTaker timer("block emerge");
157 Try to emerge it from somewhere.
159 If it is only wanted as optional, only loading from disk
164 Check if any peer wants it as non-optional. In that case it
167 Also decrement the emerge queue count in clients.
170 bool only_from_disk = true;
173 core::map<u16, u8>::Iterator i;
174 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
176 //u16 peer_id = i.getNode()->getKey();
179 u8 flags = i.getNode()->getValue();
180 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
181 only_from_disk = false;
186 if(enable_mapgen_debug_info)
187 infostream<<"EmergeThread: p="
188 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
189 <<"only_from_disk="<<only_from_disk<<std::endl;
191 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
193 //core::map<v3s16, MapBlock*> changed_blocks;
194 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
196 MapBlock *block = NULL;
197 bool got_block = true;
198 core::map<v3s16, MapBlock*> modified_blocks;
201 Fetch block from map or generate a single block
204 JMutexAutoLock envlock(m_server->m_env_mutex);
206 // Load sector if it isn't loaded
207 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
208 //map.loadSectorFull(p2d);
209 map.loadSectorMeta(p2d);
211 block = map.getBlockNoCreateNoEx(p);
212 if(!block || block->isDummy() || !block->isGenerated())
214 if(enable_mapgen_debug_info)
215 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
217 // Get, load or create sector
218 /*ServerMapSector *sector =
219 (ServerMapSector*)map.createSector(p2d);*/
221 // Load/generate block
223 /*block = map.emergeBlock(p, sector, changed_blocks,
224 lighting_invalidated_blocks);*/
226 block = map.loadBlock(p);
228 if(only_from_disk == false)
230 if(block == NULL || block->isGenerated() == false)
232 if(enable_mapgen_debug_info)
233 infostream<<"EmergeThread: generating"<<std::endl;
234 block = map.generateBlock(p, modified_blocks);
238 if(enable_mapgen_debug_info)
239 infostream<<"EmergeThread: ended up with: "
240 <<analyze_block(block)<<std::endl;
249 Ignore map edit events, they will not need to be
250 sent to anybody because the block hasn't been sent
253 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
255 // Activate objects and stuff
256 m_server->m_env->activateBlock(block, 3600);
261 /*if(block->getLightingExpired()){
262 lighting_invalidated_blocks[block->getPos()] = block;
266 // TODO: Some additional checking and lighting updating,
271 JMutexAutoLock envlock(m_server->m_env_mutex);
276 Collect a list of blocks that have been modified in
277 addition to the fetched one.
281 if(lighting_invalidated_blocks.size() > 0)
283 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
284 <<" blocks"<<std::endl;*/
286 // 50-100ms for single block generation
287 //TimeTaker timer("** EmergeThread updateLighting");
289 // Update lighting without locking the environment mutex,
290 // add modified blocks to changed blocks
291 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
294 // Add all from changed_blocks to modified_blocks
295 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
296 i.atEnd() == false; i++)
298 MapBlock *block = i.getNode()->getValue();
299 modified_blocks.insert(block->getPos(), block);
303 // If we got no block, there should be no invalidated blocks
306 //assert(lighting_invalidated_blocks.size() == 0);
312 Set sent status of modified blocks on clients
315 // NOTE: Server's clients are also behind the connection mutex
316 JMutexAutoLock lock(m_server->m_con_mutex);
319 Add the originally fetched block to the modified list
323 modified_blocks.insert(p, block);
327 Set the modified blocks unsent for all the clients
330 for(core::map<u16, RemoteClient*>::Iterator
331 i = m_server->m_clients.getIterator();
332 i.atEnd() == false; i++)
334 RemoteClient *client = i.getNode()->getValue();
336 if(modified_blocks.size() > 0)
338 // Remove block from sent history
339 client->SetBlocksNotSent(modified_blocks);
345 END_DEBUG_EXCEPTION_HANDLER(errorstream)
350 void RemoteClient::GetNextBlocks(Server *server, float dtime,
351 core::array<PrioritySortedBlockTransfer> &dest)
353 DSTACK(__FUNCTION_NAME);
356 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
359 m_nothing_to_send_pause_timer -= dtime;
360 m_nearest_unsent_reset_timer += dtime;
362 if(m_nothing_to_send_pause_timer >= 0)
367 // Won't send anything if already sending
368 if(m_blocks_sending.size() >= g_settings->getU16
369 ("max_simultaneous_block_sends_per_client"))
371 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
375 //TimeTaker timer("RemoteClient::GetNextBlocks");
377 Player *player = server->m_env->getPlayer(peer_id);
379 assert(player != NULL);
381 v3f playerpos = player->getPosition();
382 v3f playerspeed = player->getSpeed();
383 v3f playerspeeddir(0,0,0);
384 if(playerspeed.getLength() > 1.0*BS)
385 playerspeeddir = playerspeed / playerspeed.getLength();
386 // Predict to next block
387 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
389 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
391 v3s16 center = getNodeBlockPos(center_nodepos);
393 // Camera position and direction
394 v3f camera_pos = player->getEyePosition();
395 v3f camera_dir = v3f(0,0,1);
396 camera_dir.rotateYZBy(player->getPitch());
397 camera_dir.rotateXZBy(player->getYaw());
399 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
400 <<camera_dir.Z<<")"<<std::endl;*/
403 Get the starting value of the block finder radius.
406 if(m_last_center != center)
408 m_nearest_unsent_d = 0;
409 m_last_center = center;
412 /*infostream<<"m_nearest_unsent_reset_timer="
413 <<m_nearest_unsent_reset_timer<<std::endl;*/
415 // Reset periodically to workaround for some bugs or stuff
416 if(m_nearest_unsent_reset_timer > 20.0)
418 m_nearest_unsent_reset_timer = 0;
419 m_nearest_unsent_d = 0;
420 //infostream<<"Resetting m_nearest_unsent_d for "
421 // <<server->getPlayerName(peer_id)<<std::endl;
424 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
425 s16 d_start = m_nearest_unsent_d;
427 //infostream<<"d_start="<<d_start<<std::endl;
429 u16 max_simul_sends_setting = g_settings->getU16
430 ("max_simultaneous_block_sends_per_client");
431 u16 max_simul_sends_usually = max_simul_sends_setting;
434 Check the time from last addNode/removeNode.
436 Decrease send rate if player is building stuff.
438 m_time_from_building += dtime;
439 if(m_time_from_building < g_settings->getFloat(
440 "full_block_send_enable_min_time_from_building"))
442 max_simul_sends_usually
443 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
447 Number of blocks sending + number of blocks selected for sending
449 u32 num_blocks_selected = m_blocks_sending.size();
452 next time d will be continued from the d from which the nearest
453 unsent block was found this time.
455 This is because not necessarily any of the blocks found this
456 time are actually sent.
458 s32 new_nearest_unsent_d = -1;
460 s16 d_max = g_settings->getS16("max_block_send_distance");
461 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
463 // Don't loop very much at a time
464 s16 max_d_increment_at_time = 2;
465 if(d_max > d_start + max_d_increment_at_time)
466 d_max = d_start + max_d_increment_at_time;
467 /*if(d_max_gen > d_start+2)
468 d_max_gen = d_start+2;*/
470 //infostream<<"Starting from "<<d_start<<std::endl;
472 s32 nearest_emerged_d = -1;
473 s32 nearest_emergefull_d = -1;
474 s32 nearest_sent_d = -1;
475 bool queue_is_full = false;
478 for(d = d_start; d <= d_max; d++)
480 /*errorstream<<"checking d="<<d<<" for "
481 <<server->getPlayerName(peer_id)<<std::endl;*/
482 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
485 If m_nearest_unsent_d was changed by the EmergeThread
486 (it can change it to 0 through SetBlockNotSent),
488 Else update m_nearest_unsent_d
490 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
492 d = m_nearest_unsent_d;
493 last_nearest_unsent_d = m_nearest_unsent_d;
497 Get the border/face dot coordinates of a "d-radiused"
500 core::list<v3s16> list;
501 getFacePositions(list, d);
503 core::list<v3s16>::Iterator li;
504 for(li=list.begin(); li!=list.end(); li++)
506 v3s16 p = *li + center;
510 - Don't allow too many simultaneous transfers
511 - EXCEPT when the blocks are very close
513 Also, don't send blocks that are already flying.
516 // Start with the usual maximum
517 u16 max_simul_dynamic = max_simul_sends_usually;
519 // If block is very close, allow full maximum
520 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
521 max_simul_dynamic = max_simul_sends_setting;
523 // Don't select too many blocks for sending
524 if(num_blocks_selected >= max_simul_dynamic)
526 queue_is_full = true;
527 goto queue_full_break;
530 // Don't send blocks that are currently being transferred
531 if(m_blocks_sending.find(p) != NULL)
537 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
538 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
539 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
545 // If this is true, inexistent block will be made from scratch
546 bool generate = d <= d_max_gen;
549 /*// Limit the generating area vertically to 2/3
550 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
553 // Limit the send area vertically to 1/2
554 if(abs(p.Y - center.Y) > d_max / 2)
560 If block is far away, don't generate it unless it is
566 // Block center y in nodes
567 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
568 // Don't generate if it's very high or very low
569 if(y < -64 || y > 64)
573 v2s16 p2d_nodes_center(
577 // Get ground height in nodes
578 s16 gh = server->m_env->getServerMap().findGroundLevel(
581 // If differs a lot, don't generate
582 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
584 // Actually, don't even send it
590 //infostream<<"d="<<d<<std::endl;
593 Don't generate or send if not in sight
594 FIXME This only works if the client uses a small enough
595 FOV setting. The default of 72 degrees is fine.
598 float camera_fov = (72.0*PI/180) * 4./3.;
599 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
605 Don't send already sent blocks
608 if(m_blocks_sent.find(p) != NULL)
615 Check if map has this block
617 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
619 bool surely_not_found_on_disk = false;
620 bool block_is_invalid = false;
623 // Reset usage timer, this block will be of use in the future.
624 block->resetUsageTimer();
626 // Block is dummy if data doesn't exist.
627 // It means it has been not found from disk and not generated
630 surely_not_found_on_disk = true;
633 // Block is valid if lighting is up-to-date and data exists
634 if(block->isValid() == false)
636 block_is_invalid = true;
639 /*if(block->isFullyGenerated() == false)
641 block_is_invalid = true;
646 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
647 v2s16 chunkpos = map->sector_to_chunk(p2d);
648 if(map->chunkNonVolatile(chunkpos) == false)
649 block_is_invalid = true;
651 if(block->isGenerated() == false)
652 block_is_invalid = true;
655 If block is not close, don't send it unless it is near
658 Block is near ground level if night-time mesh
659 differs from day-time mesh.
663 if(block->dayNightDiffed() == false)
670 If block has been marked to not exist on disk (dummy)
671 and generating new ones is not wanted, skip block.
673 if(generate == false && surely_not_found_on_disk == true)
680 Add inexistent block to emerge queue.
682 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
684 //TODO: Get value from somewhere
685 // Allow only one block in emerge queue
686 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
687 // Allow two blocks in queue per client
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
689 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
691 //infostream<<"Adding block to emerge queue"<<std::endl;
693 // Add it to the emerge queue and trigger the thread
696 if(generate == false)
697 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
699 server->m_emerge_queue.addBlock(peer_id, p, flags);
700 server->m_emergethread.trigger();
702 if(nearest_emerged_d == -1)
703 nearest_emerged_d = d;
705 if(nearest_emergefull_d == -1)
706 nearest_emergefull_d = d;
713 if(nearest_sent_d == -1)
717 Add block to send queue
720 /*errorstream<<"sending from d="<<d<<" to "
721 <<server->getPlayerName(peer_id)<<std::endl;*/
723 PrioritySortedBlockTransfer q((float)d, p, peer_id);
727 num_blocks_selected += 1;
732 //infostream<<"Stopped at "<<d<<std::endl;
734 // If nothing was found for sending and nothing was queued for
735 // emerging, continue next time browsing from here
736 if(nearest_emerged_d != -1){
737 new_nearest_unsent_d = nearest_emerged_d;
738 } else if(nearest_emergefull_d != -1){
739 new_nearest_unsent_d = nearest_emergefull_d;
741 if(d > g_settings->getS16("max_block_send_distance")){
742 new_nearest_unsent_d = 0;
743 m_nothing_to_send_pause_timer = 2.0;
744 /*infostream<<"GetNextBlocks(): d wrapped around for "
745 <<server->getPlayerName(peer_id)
746 <<"; setting to 0 and pausing"<<std::endl;*/
748 if(nearest_sent_d != -1)
749 new_nearest_unsent_d = nearest_sent_d;
751 new_nearest_unsent_d = d;
755 if(new_nearest_unsent_d != -1)
756 m_nearest_unsent_d = new_nearest_unsent_d;
758 /*timer_result = timer.stop(true);
759 if(timer_result != 0)
760 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
763 void RemoteClient::SendObjectData(
766 core::map<v3s16, bool> &stepped_blocks
769 DSTACK(__FUNCTION_NAME);
771 // Can't send anything without knowing version
772 if(serialization_version == SER_FMT_VER_INVALID)
774 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
780 Send a TOCLIENT_OBJECTDATA packet.
784 u16 number of player positions
796 std::ostringstream os(std::ios_base::binary);
800 writeU16(buf, TOCLIENT_OBJECTDATA);
801 os.write((char*)buf, 2);
804 Get and write player data
807 // Get connected players
808 core::list<Player*> players = server->m_env->getPlayers(true);
810 // Write player count
811 u16 playercount = players.size();
812 writeU16(buf, playercount);
813 os.write((char*)buf, 2);
815 core::list<Player*>::Iterator i;
816 for(i = players.begin();
817 i != players.end(); i++)
821 v3f pf = player->getPosition();
822 v3f sf = player->getSpeed();
824 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
825 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
826 s32 pitch_i (player->getPitch() * 100);
827 s32 yaw_i (player->getYaw() * 100);
829 writeU16(buf, player->peer_id);
830 os.write((char*)buf, 2);
831 writeV3S32(buf, position_i);
832 os.write((char*)buf, 12);
833 writeV3S32(buf, speed_i);
834 os.write((char*)buf, 12);
835 writeS32(buf, pitch_i);
836 os.write((char*)buf, 4);
837 writeS32(buf, yaw_i);
838 os.write((char*)buf, 4);
842 Get and write object data (dummy, for compatibility)
847 os.write((char*)buf, 2);
853 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
856 std::string s = os.str();
857 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
858 // Send as unreliable
859 server->m_con.Send(peer_id, 0, data, false);
862 void RemoteClient::GotBlock(v3s16 p)
864 if(m_blocks_sending.find(p) != NULL)
865 m_blocks_sending.remove(p);
868 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
869 " m_blocks_sending"<<std::endl;*/
870 m_excess_gotblocks++;
872 m_blocks_sent.insert(p, true);
875 void RemoteClient::SentBlock(v3s16 p)
877 if(m_blocks_sending.find(p) == NULL)
878 m_blocks_sending.insert(p, 0.0);
880 infostream<<"RemoteClient::SentBlock(): Sent block"
881 " already in m_blocks_sending"<<std::endl;
884 void RemoteClient::SetBlockNotSent(v3s16 p)
886 m_nearest_unsent_d = 0;
888 if(m_blocks_sending.find(p) != NULL)
889 m_blocks_sending.remove(p);
890 if(m_blocks_sent.find(p) != NULL)
891 m_blocks_sent.remove(p);
894 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
896 m_nearest_unsent_d = 0;
898 for(core::map<v3s16, MapBlock*>::Iterator
899 i = blocks.getIterator();
900 i.atEnd()==false; i++)
902 v3s16 p = i.getNode()->getKey();
904 if(m_blocks_sending.find(p) != NULL)
905 m_blocks_sending.remove(p);
906 if(m_blocks_sent.find(p) != NULL)
907 m_blocks_sent.remove(p);
915 PlayerInfo::PlayerInfo()
921 void PlayerInfo::PrintLine(std::ostream *s)
924 (*s)<<"\""<<name<<"\" ("
925 <<(position.X/10)<<","<<(position.Y/10)
926 <<","<<(position.Z/10)<<") ";
928 (*s)<<" avg_rtt="<<avg_rtt;
932 u32 PIChecksum(core::list<PlayerInfo> &l)
934 core::list<PlayerInfo>::Iterator i;
937 for(i=l.begin(); i!=l.end(); i++)
939 checksum += a * (i->id+1);
940 checksum ^= 0x435aafcd;
951 std::string mapsavedir,
952 std::string configpath
955 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
956 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
957 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
961 m_emergethread(this),
963 m_time_of_day_send_timer(0),
965 m_mapsavedir(mapsavedir),
966 m_configpath(configpath),
967 m_shutdown_requested(false),
968 m_ignore_map_edit_events(false),
969 m_ignore_map_edit_events_peer_id(0)
971 m_liquid_transform_timer = 0.0;
972 m_print_info_timer = 0.0;
973 m_objectdata_timer = 0.0;
974 m_emergethread_trigger_timer = 0.0;
975 m_savemap_timer = 0.0;
979 m_step_dtime_mutex.Init();
982 JMutexAutoLock envlock(m_env_mutex);
983 JMutexAutoLock conlock(m_con_mutex);
985 // Initialize scripting
987 infostream<<"Server: Initializing scripting"<<std::endl;
988 m_lua = script_init();
991 scriptapi_export(m_lua, this);
992 // Load and run scripts
993 std::string defaultscript = porting::path_data + DIR_DELIM
994 + "scripts" + DIR_DELIM + "default.lua";
995 bool success = script_load(m_lua, defaultscript.c_str());
997 errorstream<<"Server: Failed to load and run "
998 <<defaultscript<<std::endl;
1002 // Initialize Environment
1004 m_env = new ServerEnvironment(new ServerMap(mapsavedir), m_lua);
1006 // Give environment reference to scripting api
1007 scriptapi_add_environment(m_lua, m_env);
1009 // Register us to receive map edit events
1010 m_env->getMap().addEventReceiver(this);
1012 // If file exists, load environment metadata
1013 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1015 infostream<<"Server: Loading environment metadata"<<std::endl;
1016 m_env->loadMeta(m_mapsavedir);
1020 infostream<<"Server: Loading players"<<std::endl;
1021 m_env->deSerializePlayers(m_mapsavedir);
1026 infostream<<"Server::~Server()"<<std::endl;
1029 Send shutdown message
1032 JMutexAutoLock conlock(m_con_mutex);
1034 std::wstring line = L"*** Server shutting down";
1037 Send the message to clients
1039 for(core::map<u16, RemoteClient*>::Iterator
1040 i = m_clients.getIterator();
1041 i.atEnd() == false; i++)
1043 // Get client and check that it is valid
1044 RemoteClient *client = i.getNode()->getValue();
1045 assert(client->peer_id == i.getNode()->getKey());
1046 if(client->serialization_version == SER_FMT_VER_INVALID)
1050 SendChatMessage(client->peer_id, line);
1052 catch(con::PeerNotFoundException &e)
1058 JMutexAutoLock envlock(m_env_mutex);
1063 infostream<<"Server: Saving players"<<std::endl;
1064 m_env->serializePlayers(m_mapsavedir);
1067 Save environment metadata
1069 infostream<<"Server: Saving environment metadata"<<std::endl;
1070 m_env->saveMeta(m_mapsavedir);
1082 JMutexAutoLock clientslock(m_con_mutex);
1084 for(core::map<u16, RemoteClient*>::Iterator
1085 i = m_clients.getIterator();
1086 i.atEnd() == false; i++)
1089 // NOTE: These are removed by env destructor
1091 u16 peer_id = i.getNode()->getKey();
1092 JMutexAutoLock envlock(m_env_mutex);
1093 m_env->removePlayer(peer_id);
1097 delete i.getNode()->getValue();
1101 // Delete Environment
1104 // Deinitialize scripting
1105 infostream<<"Server: Deinitializing scripting"<<std::endl;
1106 script_deinit(m_lua);
1109 void Server::start(unsigned short port)
1111 DSTACK(__FUNCTION_NAME);
1112 // Stop thread if already running
1115 // Initialize connection
1116 m_con.SetTimeoutMs(30);
1120 m_thread.setRun(true);
1123 infostream<<"Server: Started on port "<<port<<std::endl;
1128 DSTACK(__FUNCTION_NAME);
1130 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1132 // Stop threads (set run=false first so both start stopping)
1133 m_thread.setRun(false);
1134 m_emergethread.setRun(false);
1136 m_emergethread.stop();
1138 infostream<<"Server: Threads stopped"<<std::endl;
1141 void Server::step(float dtime)
1143 DSTACK(__FUNCTION_NAME);
1148 JMutexAutoLock lock(m_step_dtime_mutex);
1149 m_step_dtime += dtime;
1153 void Server::AsyncRunStep()
1155 DSTACK(__FUNCTION_NAME);
1157 g_profiler->add("Server::AsyncRunStep (num)", 1);
1161 JMutexAutoLock lock1(m_step_dtime_mutex);
1162 dtime = m_step_dtime;
1166 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1167 // Send blocks to clients
1174 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1176 //infostream<<"Server steps "<<dtime<<std::endl;
1177 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1180 JMutexAutoLock lock1(m_step_dtime_mutex);
1181 m_step_dtime -= dtime;
1188 m_uptime.set(m_uptime.get() + dtime);
1192 // Process connection's timeouts
1193 JMutexAutoLock lock2(m_con_mutex);
1194 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1195 m_con.RunTimeouts(dtime);
1199 // This has to be called so that the client list gets synced
1200 // with the peer list of the connection
1201 handlePeerChanges();
1205 Update m_time_of_day and overall game time
1208 JMutexAutoLock envlock(m_env_mutex);
1210 m_time_counter += dtime;
1211 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1212 u32 units = (u32)(m_time_counter*speed);
1213 m_time_counter -= (f32)units / speed;
1215 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1217 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1220 Send to clients at constant intervals
1223 m_time_of_day_send_timer -= dtime;
1224 if(m_time_of_day_send_timer < 0.0)
1226 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1228 //JMutexAutoLock envlock(m_env_mutex);
1229 JMutexAutoLock conlock(m_con_mutex);
1231 for(core::map<u16, RemoteClient*>::Iterator
1232 i = m_clients.getIterator();
1233 i.atEnd() == false; i++)
1235 RemoteClient *client = i.getNode()->getValue();
1236 //Player *player = m_env->getPlayer(client->peer_id);
1238 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1239 m_env->getTimeOfDay());
1241 m_con.Send(client->peer_id, 0, data, true);
1247 JMutexAutoLock lock(m_env_mutex);
1249 ScopeProfiler sp(g_profiler, "SEnv step");
1250 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1254 const float map_timer_and_unload_dtime = 5.15;
1255 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1257 JMutexAutoLock lock(m_env_mutex);
1258 // Run Map's timers and unload unused data
1259 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1260 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1261 g_settings->getFloat("server_unload_unused_data_timeout"));
1271 m_liquid_transform_timer += dtime;
1272 if(m_liquid_transform_timer >= 1.00)
1274 m_liquid_transform_timer -= 1.00;
1276 JMutexAutoLock lock(m_env_mutex);
1278 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1280 core::map<v3s16, MapBlock*> modified_blocks;
1281 m_env->getMap().transformLiquids(modified_blocks);
1286 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1287 ServerMap &map = ((ServerMap&)m_env->getMap());
1288 map.updateLighting(modified_blocks, lighting_modified_blocks);
1290 // Add blocks modified by lighting to modified_blocks
1291 for(core::map<v3s16, MapBlock*>::Iterator
1292 i = lighting_modified_blocks.getIterator();
1293 i.atEnd() == false; i++)
1295 MapBlock *block = i.getNode()->getValue();
1296 modified_blocks.insert(block->getPos(), block);
1300 Set the modified blocks unsent for all the clients
1303 JMutexAutoLock lock2(m_con_mutex);
1305 for(core::map<u16, RemoteClient*>::Iterator
1306 i = m_clients.getIterator();
1307 i.atEnd() == false; i++)
1309 RemoteClient *client = i.getNode()->getValue();
1311 if(modified_blocks.size() > 0)
1313 // Remove block from sent history
1314 client->SetBlocksNotSent(modified_blocks);
1319 // Periodically print some info
1321 float &counter = m_print_info_timer;
1327 JMutexAutoLock lock2(m_con_mutex);
1329 if(m_clients.size() != 0)
1330 infostream<<"Players:"<<std::endl;
1331 for(core::map<u16, RemoteClient*>::Iterator
1332 i = m_clients.getIterator();
1333 i.atEnd() == false; i++)
1335 //u16 peer_id = i.getNode()->getKey();
1336 RemoteClient *client = i.getNode()->getValue();
1337 Player *player = m_env->getPlayer(client->peer_id);
1340 infostream<<"* "<<player->getName()<<"\t";
1341 client->PrintInfo(infostream);
1346 //if(g_settings->getBool("enable_experimental"))
1350 Check added and deleted active objects
1353 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1354 JMutexAutoLock envlock(m_env_mutex);
1355 JMutexAutoLock conlock(m_con_mutex);
1357 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1359 // Radius inside which objects are active
1360 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1361 radius *= MAP_BLOCKSIZE;
1363 for(core::map<u16, RemoteClient*>::Iterator
1364 i = m_clients.getIterator();
1365 i.atEnd() == false; i++)
1367 RemoteClient *client = i.getNode()->getValue();
1368 Player *player = m_env->getPlayer(client->peer_id);
1371 // This can happen if the client timeouts somehow
1372 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1374 <<" has no associated player"<<std::endl;*/
1377 v3s16 pos = floatToInt(player->getPosition(), BS);
1379 core::map<u16, bool> removed_objects;
1380 core::map<u16, bool> added_objects;
1381 m_env->getRemovedActiveObjects(pos, radius,
1382 client->m_known_objects, removed_objects);
1383 m_env->getAddedActiveObjects(pos, radius,
1384 client->m_known_objects, added_objects);
1386 // Ignore if nothing happened
1387 if(removed_objects.size() == 0 && added_objects.size() == 0)
1389 //infostream<<"active objects: none changed"<<std::endl;
1393 std::string data_buffer;
1397 // Handle removed objects
1398 writeU16((u8*)buf, removed_objects.size());
1399 data_buffer.append(buf, 2);
1400 for(core::map<u16, bool>::Iterator
1401 i = removed_objects.getIterator();
1402 i.atEnd()==false; i++)
1405 u16 id = i.getNode()->getKey();
1406 ServerActiveObject* obj = m_env->getActiveObject(id);
1408 // Add to data buffer for sending
1409 writeU16((u8*)buf, i.getNode()->getKey());
1410 data_buffer.append(buf, 2);
1412 // Remove from known objects
1413 client->m_known_objects.remove(i.getNode()->getKey());
1415 if(obj && obj->m_known_by_count > 0)
1416 obj->m_known_by_count--;
1419 // Handle added objects
1420 writeU16((u8*)buf, added_objects.size());
1421 data_buffer.append(buf, 2);
1422 for(core::map<u16, bool>::Iterator
1423 i = added_objects.getIterator();
1424 i.atEnd()==false; i++)
1427 u16 id = i.getNode()->getKey();
1428 ServerActiveObject* obj = m_env->getActiveObject(id);
1431 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1433 infostream<<"WARNING: "<<__FUNCTION_NAME
1434 <<": NULL object"<<std::endl;
1436 type = obj->getType();
1438 // Add to data buffer for sending
1439 writeU16((u8*)buf, id);
1440 data_buffer.append(buf, 2);
1441 writeU8((u8*)buf, type);
1442 data_buffer.append(buf, 1);
1445 data_buffer.append(serializeLongString(
1446 obj->getClientInitializationData()));
1448 data_buffer.append(serializeLongString(""));
1450 // Add to known objects
1451 client->m_known_objects.insert(i.getNode()->getKey(), false);
1454 obj->m_known_by_count++;
1458 SharedBuffer<u8> reply(2 + data_buffer.size());
1459 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1460 memcpy((char*)&reply[2], data_buffer.c_str(),
1461 data_buffer.size());
1463 m_con.Send(client->peer_id, 0, reply, true);
1465 infostream<<"Server: Sent object remove/add: "
1466 <<removed_objects.size()<<" removed, "
1467 <<added_objects.size()<<" added, "
1468 <<"packet size is "<<reply.getSize()<<std::endl;
1473 Collect a list of all the objects known by the clients
1474 and report it back to the environment.
1477 core::map<u16, bool> all_known_objects;
1479 for(core::map<u16, RemoteClient*>::Iterator
1480 i = m_clients.getIterator();
1481 i.atEnd() == false; i++)
1483 RemoteClient *client = i.getNode()->getValue();
1484 // Go through all known objects of client
1485 for(core::map<u16, bool>::Iterator
1486 i = client->m_known_objects.getIterator();
1487 i.atEnd()==false; i++)
1489 u16 id = i.getNode()->getKey();
1490 all_known_objects[id] = true;
1494 m_env->setKnownActiveObjects(whatever);
1500 Send object messages
1503 JMutexAutoLock envlock(m_env_mutex);
1504 JMutexAutoLock conlock(m_con_mutex);
1506 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1509 // Value = data sent by object
1510 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1512 // Get active object messages from environment
1515 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1519 core::list<ActiveObjectMessage>* message_list = NULL;
1520 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1521 n = buffered_messages.find(aom.id);
1524 message_list = new core::list<ActiveObjectMessage>;
1525 buffered_messages.insert(aom.id, message_list);
1529 message_list = n->getValue();
1531 message_list->push_back(aom);
1534 // Route data to every client
1535 for(core::map<u16, RemoteClient*>::Iterator
1536 i = m_clients.getIterator();
1537 i.atEnd()==false; i++)
1539 RemoteClient *client = i.getNode()->getValue();
1540 std::string reliable_data;
1541 std::string unreliable_data;
1542 // Go through all objects in message buffer
1543 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1544 j = buffered_messages.getIterator();
1545 j.atEnd()==false; j++)
1547 // If object is not known by client, skip it
1548 u16 id = j.getNode()->getKey();
1549 if(client->m_known_objects.find(id) == NULL)
1551 // Get message list of object
1552 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1553 // Go through every message
1554 for(core::list<ActiveObjectMessage>::Iterator
1555 k = list->begin(); k != list->end(); k++)
1557 // Compose the full new data with header
1558 ActiveObjectMessage aom = *k;
1559 std::string new_data;
1562 writeU16((u8*)&buf[0], aom.id);
1563 new_data.append(buf, 2);
1565 new_data += serializeString(aom.datastring);
1566 // Add data to buffer
1568 reliable_data += new_data;
1570 unreliable_data += new_data;
1574 reliable_data and unreliable_data are now ready.
1577 if(reliable_data.size() > 0)
1579 SharedBuffer<u8> reply(2 + reliable_data.size());
1580 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1581 memcpy((char*)&reply[2], reliable_data.c_str(),
1582 reliable_data.size());
1584 m_con.Send(client->peer_id, 0, reply, true);
1586 if(unreliable_data.size() > 0)
1588 SharedBuffer<u8> reply(2 + unreliable_data.size());
1589 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1590 memcpy((char*)&reply[2], unreliable_data.c_str(),
1591 unreliable_data.size());
1592 // Send as unreliable
1593 m_con.Send(client->peer_id, 0, reply, false);
1596 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1598 infostream<<"Server: Size of object message data: "
1599 <<"reliable: "<<reliable_data.size()
1600 <<", unreliable: "<<unreliable_data.size()
1605 // Clear buffered_messages
1606 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1607 i = buffered_messages.getIterator();
1608 i.atEnd()==false; i++)
1610 delete i.getNode()->getValue();
1614 } // enable_experimental
1617 Send queued-for-sending map edit events.
1620 // Don't send too many at a time
1623 // Single change sending is disabled if queue size is not small
1624 bool disable_single_change_sending = false;
1625 if(m_unsent_map_edit_queue.size() >= 4)
1626 disable_single_change_sending = true;
1628 bool got_any_events = false;
1630 // We'll log the amount of each
1633 while(m_unsent_map_edit_queue.size() != 0)
1635 got_any_events = true;
1637 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1639 // Players far away from the change are stored here.
1640 // Instead of sending the changes, MapBlocks are set not sent
1642 core::list<u16> far_players;
1644 if(event->type == MEET_ADDNODE)
1646 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1647 prof.add("MEET_ADDNODE", 1);
1648 if(disable_single_change_sending)
1649 sendAddNode(event->p, event->n, event->already_known_by_peer,
1652 sendAddNode(event->p, event->n, event->already_known_by_peer,
1655 else if(event->type == MEET_REMOVENODE)
1657 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1658 prof.add("MEET_REMOVENODE", 1);
1659 if(disable_single_change_sending)
1660 sendRemoveNode(event->p, event->already_known_by_peer,
1663 sendRemoveNode(event->p, event->already_known_by_peer,
1666 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1668 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1669 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1670 setBlockNotSent(event->p);
1672 else if(event->type == MEET_OTHER)
1674 infostream<<"Server: MEET_OTHER"<<std::endl;
1675 prof.add("MEET_OTHER", 1);
1676 for(core::map<v3s16, bool>::Iterator
1677 i = event->modified_blocks.getIterator();
1678 i.atEnd()==false; i++)
1680 v3s16 p = i.getNode()->getKey();
1686 prof.add("unknown", 1);
1687 infostream<<"WARNING: Server: Unknown MapEditEvent "
1688 <<((u32)event->type)<<std::endl;
1692 Set blocks not sent to far players
1694 if(far_players.size() > 0)
1696 // Convert list format to that wanted by SetBlocksNotSent
1697 core::map<v3s16, MapBlock*> modified_blocks2;
1698 for(core::map<v3s16, bool>::Iterator
1699 i = event->modified_blocks.getIterator();
1700 i.atEnd()==false; i++)
1702 v3s16 p = i.getNode()->getKey();
1703 modified_blocks2.insert(p,
1704 m_env->getMap().getBlockNoCreateNoEx(p));
1706 // Set blocks not sent
1707 for(core::list<u16>::Iterator
1708 i = far_players.begin();
1709 i != far_players.end(); i++)
1712 RemoteClient *client = getClient(peer_id);
1715 client->SetBlocksNotSent(modified_blocks2);
1721 /*// Don't send too many at a time
1723 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1729 infostream<<"Server: MapEditEvents:"<<std::endl;
1730 prof.print(infostream);
1736 Send object positions
1739 float &counter = m_objectdata_timer;
1741 if(counter >= g_settings->getFloat("objectdata_interval"))
1743 JMutexAutoLock lock1(m_env_mutex);
1744 JMutexAutoLock lock2(m_con_mutex);
1746 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1748 SendObjectData(counter);
1755 Trigger emergethread (it somehow gets to a non-triggered but
1756 bysy state sometimes)
1759 float &counter = m_emergethread_trigger_timer;
1765 m_emergethread.trigger();
1769 // Save map, players and auth stuff
1771 float &counter = m_savemap_timer;
1773 if(counter >= g_settings->getFloat("server_map_save_interval"))
1777 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1780 if(m_authmanager.isModified())
1781 m_authmanager.save();
1784 if(m_banmanager.isModified())
1785 m_banmanager.save();
1788 JMutexAutoLock lock(m_env_mutex);
1790 /*// Unload unused data (delete from memory)
1791 m_env->getMap().unloadUnusedData(
1792 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1794 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1795 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1798 // Save only changed parts
1799 m_env->getMap().save(true);
1801 /*if(deleted_count > 0)
1803 infostream<<"Server: Unloaded "<<deleted_count
1804 <<" blocks from memory"<<std::endl;
1808 m_env->serializePlayers(m_mapsavedir);
1810 // Save environment metadata
1811 m_env->saveMeta(m_mapsavedir);
1816 void Server::Receive()
1818 DSTACK(__FUNCTION_NAME);
1819 SharedBuffer<u8> data;
1824 JMutexAutoLock conlock(m_con_mutex);
1825 datasize = m_con.Receive(peer_id, data);
1828 // This has to be called so that the client list gets synced
1829 // with the peer list of the connection
1830 handlePeerChanges();
1832 ProcessData(*data, datasize, peer_id);
1834 catch(con::InvalidIncomingDataException &e)
1836 infostream<<"Server::Receive(): "
1837 "InvalidIncomingDataException: what()="
1838 <<e.what()<<std::endl;
1840 catch(con::PeerNotFoundException &e)
1842 //NOTE: This is not needed anymore
1844 // The peer has been disconnected.
1845 // Find the associated player and remove it.
1847 /*JMutexAutoLock envlock(m_env_mutex);
1849 infostream<<"ServerThread: peer_id="<<peer_id
1850 <<" has apparently closed connection. "
1851 <<"Removing player."<<std::endl;
1853 m_env->removePlayer(peer_id);*/
1857 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1859 DSTACK(__FUNCTION_NAME);
1860 // Environment is locked first.
1861 JMutexAutoLock envlock(m_env_mutex);
1862 JMutexAutoLock conlock(m_con_mutex);
1865 Address address = m_con.GetPeerAddress(peer_id);
1867 // drop player if is ip is banned
1868 if(m_banmanager.isIpBanned(address.serializeString())){
1869 SendAccessDenied(m_con, peer_id,
1870 L"Your ip is banned. Banned name was "
1871 +narrow_to_wide(m_banmanager.getBanName(
1872 address.serializeString())));
1873 m_con.DeletePeer(peer_id);
1877 catch(con::PeerNotFoundException &e)
1879 infostream<<"Server::ProcessData(): Cancelling: peer "
1880 <<peer_id<<" not found"<<std::endl;
1884 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1892 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1894 if(command == TOSERVER_INIT)
1896 // [0] u16 TOSERVER_INIT
1897 // [2] u8 SER_FMT_VER_HIGHEST
1898 // [3] u8[20] player_name
1899 // [23] u8[28] password <--- can be sent without this, from old versions
1901 if(datasize < 2+1+PLAYERNAME_SIZE)
1904 infostream<<"Server: Got TOSERVER_INIT from "
1905 <<peer_id<<std::endl;
1907 // First byte after command is maximum supported
1908 // serialization version
1909 u8 client_max = data[2];
1910 u8 our_max = SER_FMT_VER_HIGHEST;
1911 // Use the highest version supported by both
1912 u8 deployed = core::min_(client_max, our_max);
1913 // If it's lower than the lowest supported, give up.
1914 if(deployed < SER_FMT_VER_LOWEST)
1915 deployed = SER_FMT_VER_INVALID;
1917 //peer->serialization_version = deployed;
1918 getClient(peer_id)->pending_serialization_version = deployed;
1920 if(deployed == SER_FMT_VER_INVALID)
1922 infostream<<"Server: Cannot negotiate "
1923 "serialization version with peer "
1924 <<peer_id<<std::endl;
1925 SendAccessDenied(m_con, peer_id,
1926 L"Your client is too old (map format)");
1931 Read and check network protocol version
1934 u16 net_proto_version = 0;
1935 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1937 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1940 getClient(peer_id)->net_proto_version = net_proto_version;
1942 if(net_proto_version == 0)
1944 SendAccessDenied(m_con, peer_id,
1945 L"Your client is too old. Please upgrade.");
1949 /* Uhh... this should actually be a warning but let's do it like this */
1950 if(g_settings->getBool("strict_protocol_version_checking"))
1952 if(net_proto_version < PROTOCOL_VERSION)
1954 SendAccessDenied(m_con, peer_id,
1955 L"Your client is too old. Please upgrade.");
1965 char playername[PLAYERNAME_SIZE];
1966 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1968 playername[i] = data[3+i];
1970 playername[PLAYERNAME_SIZE-1] = 0;
1972 if(playername[0]=='\0')
1974 infostream<<"Server: Player has empty name"<<std::endl;
1975 SendAccessDenied(m_con, peer_id,
1980 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1982 infostream<<"Server: Player has invalid name"<<std::endl;
1983 SendAccessDenied(m_con, peer_id,
1984 L"Name contains unallowed characters");
1989 char password[PASSWORD_SIZE];
1990 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1992 // old version - assume blank password
1997 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1999 password[i] = data[23+i];
2001 password[PASSWORD_SIZE-1] = 0;
2004 std::string checkpwd;
2005 if(m_authmanager.exists(playername))
2007 checkpwd = m_authmanager.getPassword(playername);
2011 checkpwd = g_settings->get("default_password");
2014 /*infostream<<"Server: Client gave password '"<<password
2015 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2017 if(password != checkpwd && m_authmanager.exists(playername))
2019 infostream<<"Server: peer_id="<<peer_id
2020 <<": supplied invalid password for "
2021 <<playername<<std::endl;
2022 SendAccessDenied(m_con, peer_id, L"Invalid password");
2026 // Add player to auth manager
2027 if(m_authmanager.exists(playername) == false)
2029 infostream<<"Server: adding player "<<playername
2030 <<" to auth manager"<<std::endl;
2031 m_authmanager.add(playername);
2032 m_authmanager.setPassword(playername, checkpwd);
2033 m_authmanager.setPrivs(playername,
2034 stringToPrivs(g_settings->get("default_privs")));
2035 m_authmanager.save();
2038 // Enforce user limit.
2039 // Don't enforce for users that have some admin right
2040 if(m_clients.size() >= g_settings->getU16("max_users") &&
2041 (m_authmanager.getPrivs(playername)
2042 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2043 playername != g_settings->get("name"))
2045 SendAccessDenied(m_con, peer_id, L"Too many users.");
2050 Player *player = emergePlayer(playername, password, peer_id);
2052 // If failed, cancel
2055 infostream<<"Server: peer_id="<<peer_id
2056 <<": failed to emerge player"<<std::endl;
2061 Answer with a TOCLIENT_INIT
2064 SharedBuffer<u8> reply(2+1+6+8);
2065 writeU16(&reply[0], TOCLIENT_INIT);
2066 writeU8(&reply[2], deployed);
2067 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2068 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2071 m_con.Send(peer_id, 0, reply, true);
2075 Send complete position information
2077 SendMovePlayer(player);
2082 if(command == TOSERVER_INIT2)
2084 infostream<<"Server: Got TOSERVER_INIT2 from "
2085 <<peer_id<<std::endl;
2088 getClient(peer_id)->serialization_version
2089 = getClient(peer_id)->pending_serialization_version;
2092 Send some initialization data
2095 // Send player info to all players
2098 // Send inventory to player
2099 UpdateCrafting(peer_id);
2100 SendInventory(peer_id);
2102 // Send player items to all players
2105 Player *player = m_env->getPlayer(peer_id);
2108 SendPlayerHP(player);
2112 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2113 m_env->getTimeOfDay());
2114 m_con.Send(peer_id, 0, data, true);
2117 // Send information about server to player in chat
2118 SendChatMessage(peer_id, getStatusString());
2120 // Send information about joining in chat
2122 std::wstring name = L"unknown";
2123 Player *player = m_env->getPlayer(peer_id);
2125 name = narrow_to_wide(player->getName());
2127 std::wstring message;
2130 message += L" joined game";
2131 BroadcastChatMessage(message);
2134 // Warnings about protocol version can be issued here
2135 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2137 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2141 Check HP, respawn if necessary
2143 HandlePlayerHP(player, 0);
2149 std::ostringstream os(std::ios_base::binary);
2150 for(core::map<u16, RemoteClient*>::Iterator
2151 i = m_clients.getIterator();
2152 i.atEnd() == false; i++)
2154 RemoteClient *client = i.getNode()->getValue();
2155 assert(client->peer_id == i.getNode()->getKey());
2156 if(client->serialization_version == SER_FMT_VER_INVALID)
2159 Player *player = m_env->getPlayer(client->peer_id);
2162 // Get name of player
2163 os<<player->getName()<<" ";
2166 actionstream<<player->getName()<<" joins game. List of players: "
2167 <<os.str()<<std::endl;
2173 if(peer_ser_ver == SER_FMT_VER_INVALID)
2175 infostream<<"Server::ProcessData(): Cancelling: Peer"
2176 " serialization format invalid or not initialized."
2177 " Skipping incoming command="<<command<<std::endl;
2181 Player *player = m_env->getPlayer(peer_id);
2184 infostream<<"Server::ProcessData(): Cancelling: "
2185 "No player for peer_id="<<peer_id
2189 if(command == TOSERVER_PLAYERPOS)
2191 if(datasize < 2+12+12+4+4)
2195 v3s32 ps = readV3S32(&data[start+2]);
2196 v3s32 ss = readV3S32(&data[start+2+12]);
2197 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2198 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2199 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2200 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2201 pitch = wrapDegrees(pitch);
2202 yaw = wrapDegrees(yaw);
2204 player->setPosition(position);
2205 player->setSpeed(speed);
2206 player->setPitch(pitch);
2207 player->setYaw(yaw);
2209 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2210 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2211 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2213 else if(command == TOSERVER_GOTBLOCKS)
2226 u16 count = data[2];
2227 for(u16 i=0; i<count; i++)
2229 if((s16)datasize < 2+1+(i+1)*6)
2230 throw con::InvalidIncomingDataException
2231 ("GOTBLOCKS length is too short");
2232 v3s16 p = readV3S16(&data[2+1+i*6]);
2233 /*infostream<<"Server: GOTBLOCKS ("
2234 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2235 RemoteClient *client = getClient(peer_id);
2236 client->GotBlock(p);
2239 else if(command == TOSERVER_DELETEDBLOCKS)
2252 u16 count = data[2];
2253 for(u16 i=0; i<count; i++)
2255 if((s16)datasize < 2+1+(i+1)*6)
2256 throw con::InvalidIncomingDataException
2257 ("DELETEDBLOCKS length is too short");
2258 v3s16 p = readV3S16(&data[2+1+i*6]);
2259 /*infostream<<"Server: DELETEDBLOCKS ("
2260 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2261 RemoteClient *client = getClient(peer_id);
2262 client->SetBlockNotSent(p);
2265 else if(command == TOSERVER_CLICK_OBJECT)
2267 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2270 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2275 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2281 [2] u8 button (0=left, 1=right)
2285 u8 button = readU8(&data[2]);
2286 u16 id = readS16(&data[3]);
2287 u16 item_i = readU16(&data[5]);
2289 ServerActiveObject *obj = m_env->getActiveObject(id);
2293 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2298 // Skip if object has been removed
2302 //TODO: Check that object is reasonably close
2304 // Get ServerRemotePlayer
2305 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2307 // Update wielded item
2308 srp->wieldItem(item_i);
2310 // Left click, pick/punch
2313 actionstream<<player->getName()<<" punches object "
2314 <<obj->getId()<<std::endl;
2321 Try creating inventory item
2323 InventoryItem *item = obj->createPickedUpItem();
2327 InventoryList *ilist = player->inventory.getList("main");
2330 actionstream<<player->getName()<<" picked up "
2331 <<item->getName()<<std::endl;
2332 if(g_settings->getBool("creative_mode") == false)
2334 // Skip if inventory has no free space
2335 if(ilist->roomForItem(item) == false)
2337 infostream<<"Player inventory has no free space"<<std::endl;
2341 // Add to inventory and send inventory
2342 ilist->addItem(item);
2343 UpdateCrafting(player->peer_id);
2344 SendInventory(player->peer_id);
2347 // Remove object from environment
2348 obj->m_removed = true;
2354 Item cannot be picked up. Punch it instead.
2357 actionstream<<player->getName()<<" punches object "
2358 <<obj->getId()<<std::endl;
2360 ToolItem *titem = NULL;
2361 std::string toolname = "";
2363 InventoryList *mlist = player->inventory.getList("main");
2366 InventoryItem *item = mlist->getItem(item_i);
2367 if(item && (std::string)item->getName() == "ToolItem")
2369 titem = (ToolItem*)item;
2370 toolname = titem->getToolName();
2374 v3f playerpos = player->getPosition();
2375 v3f objpos = obj->getBasePosition();
2376 v3f dir = (objpos - playerpos).normalize();
2378 u16 wear = obj->punch(toolname, dir, player->getName());
2382 bool weared_out = titem->addWear(wear);
2384 mlist->deleteItem(item_i);
2385 SendInventory(player->peer_id);
2390 // Right click, do something with object
2393 actionstream<<player->getName()<<" right clicks object "
2394 <<obj->getId()<<std::endl;
2397 obj->rightClick(srp);
2401 Update player state to client
2403 SendPlayerHP(player);
2404 UpdateCrafting(player->peer_id);
2405 SendInventory(player->peer_id);
2407 else if(command == TOSERVER_GROUND_ACTION)
2415 [3] v3s16 nodepos_undersurface
2416 [9] v3s16 nodepos_abovesurface
2421 2: stop digging (all parameters ignored)
2422 3: digging completed
2424 u8 action = readU8(&data[2]);
2426 p_under.X = readS16(&data[3]);
2427 p_under.Y = readS16(&data[5]);
2428 p_under.Z = readS16(&data[7]);
2430 p_over.X = readS16(&data[9]);
2431 p_over.Y = readS16(&data[11]);
2432 p_over.Z = readS16(&data[13]);
2433 u16 item_i = readU16(&data[15]);
2435 //TODO: Check that target is reasonably close
2443 NOTE: This can be used in the future to check if
2444 somebody is cheating, by checking the timing.
2451 else if(action == 2)
2454 RemoteClient *client = getClient(peer_id);
2455 JMutexAutoLock digmutex(client->m_dig_mutex);
2456 client->m_dig_tool_item = -1;
2461 3: Digging completed
2463 else if(action == 3)
2465 // Mandatory parameter; actually used for nothing
2466 core::map<v3s16, MapBlock*> modified_blocks;
2468 content_t material = CONTENT_IGNORE;
2469 u8 mineral = MINERAL_NONE;
2471 bool cannot_remove_node = false;
2475 MapNode n = m_env->getMap().getNode(p_under);
2477 mineral = n.getMineral();
2478 // Get material at position
2479 material = n.getContent();
2480 // If not yet cancelled
2481 if(cannot_remove_node == false)
2483 // If it's not diggable, do nothing
2484 if(content_diggable(material) == false)
2486 infostream<<"Server: Not finishing digging: "
2487 <<"Node not diggable"
2489 cannot_remove_node = true;
2492 // If not yet cancelled
2493 if(cannot_remove_node == false)
2495 // Get node metadata
2496 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2497 if(meta && meta->nodeRemovalDisabled() == true)
2499 infostream<<"Server: Not finishing digging: "
2500 <<"Node metadata disables removal"
2502 cannot_remove_node = true;
2506 catch(InvalidPositionException &e)
2508 infostream<<"Server: Not finishing digging: Node not found."
2509 <<" Adding block to emerge queue."
2511 m_emerge_queue.addBlock(peer_id,
2512 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2513 cannot_remove_node = true;
2516 // Make sure the player is allowed to do it
2517 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2519 infostream<<"Player "<<player->getName()<<" cannot remove node"
2520 <<" because privileges are "<<getPlayerPrivs(player)
2522 cannot_remove_node = true;
2526 If node can't be removed, set block to be re-sent to
2529 if(cannot_remove_node)
2531 infostream<<"Server: Not finishing digging."<<std::endl;
2533 // Client probably has wrong data.
2534 // Set block not sent, so that client will get
2536 infostream<<"Client "<<peer_id<<" tried to dig "
2537 <<"node; but node cannot be removed."
2538 <<" setting MapBlock not sent."<<std::endl;
2539 RemoteClient *client = getClient(peer_id);
2540 v3s16 blockpos = getNodeBlockPos(p_under);
2541 client->SetBlockNotSent(blockpos);
2546 actionstream<<player->getName()<<" digs "<<PP(p_under)
2547 <<", gets material "<<(int)material<<", mineral "
2548 <<(int)mineral<<std::endl;
2551 Send the removal to all close-by players.
2552 - If other player is close, send REMOVENODE
2553 - Otherwise set blocks not sent
2555 core::list<u16> far_players;
2556 sendRemoveNode(p_under, peer_id, &far_players, 30);
2559 Update and send inventory
2562 if(g_settings->getBool("creative_mode") == false)
2567 InventoryList *mlist = player->inventory.getList("main");
2570 InventoryItem *item = mlist->getItem(item_i);
2571 if(item && (std::string)item->getName() == "ToolItem")
2573 ToolItem *titem = (ToolItem*)item;
2574 std::string toolname = titem->getToolName();
2576 // Get digging properties for material and tool
2577 DiggingProperties prop =
2578 getDiggingProperties(material, toolname);
2580 if(prop.diggable == false)
2582 infostream<<"Server: WARNING: Player digged"
2583 <<" with impossible material + tool"
2584 <<" combination"<<std::endl;
2587 bool weared_out = titem->addWear(prop.wear);
2591 mlist->deleteItem(item_i);
2597 Add dug item to inventory
2600 InventoryItem *item = NULL;
2602 if(mineral != MINERAL_NONE)
2603 item = getDiggedMineralItem(mineral);
2608 std::string &dug_s = content_features(material).dug_item;
2611 std::istringstream is(dug_s, std::ios::binary);
2612 item = InventoryItem::deSerialize(is);
2618 // Add a item to inventory
2619 player->inventory.addItem("main", item);
2622 UpdateCrafting(player->peer_id);
2623 SendInventory(player->peer_id);
2628 if(mineral != MINERAL_NONE)
2629 item = getDiggedMineralItem(mineral);
2634 std::string &extra_dug_s = content_features(material).extra_dug_item;
2635 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2636 if(extra_dug_s != "" && extra_rarity != 0
2637 && myrand() % extra_rarity == 0)
2639 std::istringstream is(extra_dug_s, std::ios::binary);
2640 item = InventoryItem::deSerialize(is);
2646 // Add a item to inventory
2647 player->inventory.addItem("main", item);
2650 UpdateCrafting(player->peer_id);
2651 SendInventory(player->peer_id);
2657 (this takes some time so it is done after the quick stuff)
2660 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2662 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2665 Set blocks not sent to far players
2667 for(core::list<u16>::Iterator
2668 i = far_players.begin();
2669 i != far_players.end(); i++)
2672 RemoteClient *client = getClient(peer_id);
2675 client->SetBlocksNotSent(modified_blocks);
2682 else if(action == 1)
2685 InventoryList *ilist = player->inventory.getList("main");
2690 InventoryItem *item = ilist->getItem(item_i);
2692 // If there is no item, it is not possible to add it anywhere
2697 Handle material items
2699 if(std::string("MaterialItem") == item->getName())
2702 // Don't add a node if this is not a free space
2703 MapNode n2 = m_env->getMap().getNode(p_over);
2704 bool no_enough_privs =
2705 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2707 infostream<<"Player "<<player->getName()<<" cannot add node"
2708 <<" because privileges are "<<getPlayerPrivs(player)
2711 if(content_features(n2).buildable_to == false
2714 // Client probably has wrong data.
2715 // Set block not sent, so that client will get
2717 infostream<<"Client "<<peer_id<<" tried to place"
2718 <<" node in invalid position; setting"
2719 <<" MapBlock not sent."<<std::endl;
2720 RemoteClient *client = getClient(peer_id);
2721 v3s16 blockpos = getNodeBlockPos(p_over);
2722 client->SetBlockNotSent(blockpos);
2726 catch(InvalidPositionException &e)
2728 infostream<<"Server: Ignoring ADDNODE: Node not found"
2729 <<" Adding block to emerge queue."
2731 m_emerge_queue.addBlock(peer_id,
2732 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2736 // Reset build time counter
2737 getClient(peer_id)->m_time_from_building = 0.0;
2740 MaterialItem *mitem = (MaterialItem*)item;
2742 n.setContent(mitem->getMaterial());
2744 actionstream<<player->getName()<<" places material "
2745 <<(int)mitem->getMaterial()
2746 <<" at "<<PP(p_under)<<std::endl;
2748 // Calculate direction for wall mounted stuff
2749 if(content_features(n).wall_mounted)
2750 n.param2 = packDir(p_under - p_over);
2752 // Calculate the direction for furnaces and chests and stuff
2753 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2755 v3f playerpos = player->getPosition();
2756 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2757 blockpos = blockpos.normalize();
2759 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2773 Send to all close-by players
2775 core::list<u16> far_players;
2776 sendAddNode(p_over, n, 0, &far_players, 30);
2781 InventoryList *ilist = player->inventory.getList("main");
2782 if(g_settings->getBool("creative_mode") == false && ilist)
2784 // Remove from inventory and send inventory
2785 if(mitem->getCount() == 1)
2786 ilist->deleteItem(item_i);
2790 UpdateCrafting(peer_id);
2791 SendInventory(peer_id);
2797 This takes some time so it is done after the quick stuff
2799 core::map<v3s16, MapBlock*> modified_blocks;
2801 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2803 std::string p_name = std::string(player->getName());
2804 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2807 Set blocks not sent to far players
2809 for(core::list<u16>::Iterator
2810 i = far_players.begin();
2811 i != far_players.end(); i++)
2814 RemoteClient *client = getClient(peer_id);
2817 client->SetBlocksNotSent(modified_blocks);
2821 Calculate special events
2824 /*if(n.d == CONTENT_MESE)
2827 for(s16 z=-1; z<=1; z++)
2828 for(s16 y=-1; y<=1; y++)
2829 for(s16 x=-1; x<=1; x++)
2836 Place other item (not a block)
2840 v3s16 blockpos = getNodeBlockPos(p_over);
2843 Check that the block is loaded so that the item
2844 can properly be added to the static list too
2846 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2849 infostream<<"Error while placing object: "
2850 "block not found"<<std::endl;
2855 If in creative mode, item dropping is disabled unless
2856 player has build privileges
2858 if(g_settings->getBool("creative_mode") &&
2859 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2861 infostream<<"Not allowing player to drop item: "
2862 "creative mode and no build privs"<<std::endl;
2866 // Calculate a position for it
2867 v3f pos = intToFloat(p_over, BS);
2869 /*pos.Y -= BS*0.25; // let it drop a bit
2871 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2872 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2877 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2881 infostream<<"WARNING: item resulted in NULL object, "
2882 <<"not placing onto map"
2887 actionstream<<player->getName()<<" places "<<item->getName()
2888 <<" at "<<PP(p_over)<<std::endl;
2890 // Add the object to the environment
2891 m_env->addActiveObject(obj);
2893 infostream<<"Placed object"<<std::endl;
2895 if(g_settings->getBool("creative_mode") == false)
2897 // Delete the right amount of items from the slot
2898 u16 dropcount = item->getDropCount();
2900 // Delete item if all gone
2901 if(item->getCount() <= dropcount)
2903 if(item->getCount() < dropcount)
2904 infostream<<"WARNING: Server: dropped more items"
2905 <<" than the slot contains"<<std::endl;
2907 InventoryList *ilist = player->inventory.getList("main");
2909 // Remove from inventory and send inventory
2910 ilist->deleteItem(item_i);
2912 // Else decrement it
2914 item->remove(dropcount);
2917 UpdateCrafting(peer_id);
2918 SendInventory(peer_id);
2926 Catch invalid actions
2930 infostream<<"WARNING: Server: Invalid action "
2931 <<action<<std::endl;
2935 else if(command == TOSERVER_RELEASE)
2944 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2947 else if(command == TOSERVER_SIGNTEXT)
2949 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2953 else if(command == TOSERVER_SIGNNODETEXT)
2955 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2963 std::string datastring((char*)&data[2], datasize-2);
2964 std::istringstream is(datastring, std::ios_base::binary);
2967 is.read((char*)buf, 6);
2968 v3s16 p = readV3S16(buf);
2969 is.read((char*)buf, 2);
2970 u16 textlen = readU16(buf);
2972 for(u16 i=0; i<textlen; i++)
2974 is.read((char*)buf, 1);
2975 text += (char)buf[0];
2978 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2981 if(meta->typeId() != CONTENT_SIGN_WALL)
2983 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2984 signmeta->setText(text);
2986 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2987 <<" at "<<PP(p)<<std::endl;
2989 v3s16 blockpos = getNodeBlockPos(p);
2990 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2993 block->setChangedFlag();
2996 for(core::map<u16, RemoteClient*>::Iterator
2997 i = m_clients.getIterator();
2998 i.atEnd()==false; i++)
3000 RemoteClient *client = i.getNode()->getValue();
3001 client->SetBlockNotSent(blockpos);
3004 else if(command == TOSERVER_INVENTORY_ACTION)
3006 /*// Ignore inventory changes if in creative mode
3007 if(g_settings->getBool("creative_mode") == true)
3009 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3013 // Strip command and create a stream
3014 std::string datastring((char*)&data[2], datasize-2);
3015 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3016 std::istringstream is(datastring, std::ios_base::binary);
3018 InventoryAction *a = InventoryAction::deSerialize(is);
3023 c.current_player = player;
3026 Handle craftresult specially if not in creative mode
3028 bool disable_action = false;
3029 if(a->getType() == IACTION_MOVE
3030 && g_settings->getBool("creative_mode") == false)
3032 IMoveAction *ma = (IMoveAction*)a;
3033 if(ma->to_inv == "current_player" &&
3034 ma->from_inv == "current_player")
3036 InventoryList *rlist = player->inventory.getList("craftresult");
3038 InventoryList *clist = player->inventory.getList("craft");
3040 InventoryList *mlist = player->inventory.getList("main");
3043 Craftresult is no longer preview if something
3046 if(ma->to_list == "craftresult"
3047 && ma->from_list != "craftresult")
3049 // If it currently is a preview, remove
3051 if(player->craftresult_is_preview)
3053 rlist->deleteItem(0);
3055 player->craftresult_is_preview = false;
3058 Crafting takes place if this condition is true.
3060 if(player->craftresult_is_preview &&
3061 ma->from_list == "craftresult")
3063 player->craftresult_is_preview = false;
3064 clist->decrementMaterials(1);
3066 /* Print out action */
3067 InventoryList *list =
3068 player->inventory.getList("craftresult");
3070 InventoryItem *item = list->getItem(0);
3071 std::string itemname = "NULL";
3073 itemname = item->getName();
3074 actionstream<<player->getName()<<" crafts "
3075 <<itemname<<std::endl;
3078 If the craftresult is placed on itself, move it to
3079 main inventory instead of doing the action
3081 if(ma->to_list == "craftresult"
3082 && ma->from_list == "craftresult")
3084 disable_action = true;
3086 InventoryItem *item1 = rlist->changeItem(0, NULL);
3087 mlist->addItem(item1);
3090 // Disallow moving items if not allowed to build
3091 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3095 // if it's a locking chest, only allow the owner or server admins to move items
3096 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3098 Strfnd fn(ma->from_inv);
3099 std::string id0 = fn.next(":");
3100 if(id0 == "nodemeta")
3103 p.X = stoi(fn.next(","));
3104 p.Y = stoi(fn.next(","));
3105 p.Z = stoi(fn.next(","));
3106 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3107 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3108 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3109 if (lcm->getOwner() != player->getName())
3114 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3116 Strfnd fn(ma->to_inv);
3117 std::string id0 = fn.next(":");
3118 if(id0 == "nodemeta")
3121 p.X = stoi(fn.next(","));
3122 p.Y = stoi(fn.next(","));
3123 p.Z = stoi(fn.next(","));
3124 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3125 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3126 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3127 if (lcm->getOwner() != player->getName())
3134 if(disable_action == false)
3136 // Feed action to player inventory
3144 UpdateCrafting(player->peer_id);
3145 SendInventory(player->peer_id);
3150 infostream<<"TOSERVER_INVENTORY_ACTION: "
3151 <<"InventoryAction::deSerialize() returned NULL"
3155 else if(command == TOSERVER_CHAT_MESSAGE)
3163 std::string datastring((char*)&data[2], datasize-2);
3164 std::istringstream is(datastring, std::ios_base::binary);
3167 is.read((char*)buf, 2);
3168 u16 len = readU16(buf);
3170 std::wstring message;
3171 for(u16 i=0; i<len; i++)
3173 is.read((char*)buf, 2);
3174 message += (wchar_t)readU16(buf);
3177 // Get player name of this client
3178 std::wstring name = narrow_to_wide(player->getName());
3180 // Line to send to players
3182 // Whether to send to the player that sent the line
3183 bool send_to_sender = false;
3184 // Whether to send to other players
3185 bool send_to_others = false;
3187 // Local player gets all privileges regardless of
3188 // what's set on their account.
3189 u64 privs = getPlayerPrivs(player);
3192 if(message[0] == L'/')
3194 size_t strip_size = 1;
3195 if (message[1] == L'#') // support old-style commans
3197 message = message.substr(strip_size);
3199 WStrfnd f1(message);
3200 f1.next(L" "); // Skip over /#whatever
3201 std::wstring paramstring = f1.next(L"");
3203 ServerCommandContext *ctx = new ServerCommandContext(
3204 str_split(message, L' '),
3211 std::wstring reply(processServerCommand(ctx));
3212 send_to_sender = ctx->flags & SEND_TO_SENDER;
3213 send_to_others = ctx->flags & SEND_TO_OTHERS;
3215 if (ctx->flags & SEND_NO_PREFIX)
3218 line += L"Server: " + reply;
3225 if(privs & PRIV_SHOUT)
3231 send_to_others = true;
3235 line += L"Server: You are not allowed to shout";
3236 send_to_sender = true;
3243 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3246 Send the message to clients
3248 for(core::map<u16, RemoteClient*>::Iterator
3249 i = m_clients.getIterator();
3250 i.atEnd() == false; i++)
3252 // Get client and check that it is valid
3253 RemoteClient *client = i.getNode()->getValue();
3254 assert(client->peer_id == i.getNode()->getKey());
3255 if(client->serialization_version == SER_FMT_VER_INVALID)
3259 bool sender_selected = (peer_id == client->peer_id);
3260 if(sender_selected == true && send_to_sender == false)
3262 if(sender_selected == false && send_to_others == false)
3265 SendChatMessage(client->peer_id, line);
3269 else if(command == TOSERVER_DAMAGE)
3271 std::string datastring((char*)&data[2], datasize-2);
3272 std::istringstream is(datastring, std::ios_base::binary);
3273 u8 damage = readU8(is);
3275 if(g_settings->getBool("enable_damage"))
3277 actionstream<<player->getName()<<" damaged by "
3278 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3281 HandlePlayerHP(player, damage);
3285 SendPlayerHP(player);
3288 else if(command == TOSERVER_PASSWORD)
3291 [0] u16 TOSERVER_PASSWORD
3292 [2] u8[28] old password
3293 [30] u8[28] new password
3296 if(datasize != 2+PASSWORD_SIZE*2)
3298 /*char password[PASSWORD_SIZE];
3299 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3300 password[i] = data[2+i];
3301 password[PASSWORD_SIZE-1] = 0;*/
3303 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3311 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3313 char c = data[2+PASSWORD_SIZE+i];
3319 infostream<<"Server: Client requests a password change from "
3320 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3322 std::string playername = player->getName();
3324 if(m_authmanager.exists(playername) == false)
3326 infostream<<"Server: playername not found in authmanager"<<std::endl;
3327 // Wrong old password supplied!!
3328 SendChatMessage(peer_id, L"playername not found in authmanager");
3332 std::string checkpwd = m_authmanager.getPassword(playername);
3334 if(oldpwd != checkpwd)
3336 infostream<<"Server: invalid old password"<<std::endl;
3337 // Wrong old password supplied!!
3338 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3342 actionstream<<player->getName()<<" changes password"<<std::endl;
3344 m_authmanager.setPassword(playername, newpwd);
3346 infostream<<"Server: password change successful for "<<playername
3348 SendChatMessage(peer_id, L"Password change successful");
3350 else if(command == TOSERVER_PLAYERITEM)
3355 u16 item = readU16(&data[2]);
3356 player->wieldItem(item);
3357 SendWieldedItem(player);
3359 else if(command == TOSERVER_RESPAWN)
3364 RespawnPlayer(player);
3366 actionstream<<player->getName()<<" respawns at "
3367 <<PP(player->getPosition()/BS)<<std::endl;
3371 infostream<<"Server::ProcessData(): Ignoring "
3372 "unknown command "<<command<<std::endl;
3376 catch(SendFailedException &e)
3378 errorstream<<"Server::ProcessData(): SendFailedException: "
3384 void Server::onMapEditEvent(MapEditEvent *event)
3386 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3387 if(m_ignore_map_edit_events)
3389 MapEditEvent *e = event->clone();
3390 m_unsent_map_edit_queue.push_back(e);
3393 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3395 if(id == "current_player")
3397 assert(c->current_player);
3398 return &(c->current_player->inventory);
3402 std::string id0 = fn.next(":");
3404 if(id0 == "nodemeta")
3407 p.X = stoi(fn.next(","));
3408 p.Y = stoi(fn.next(","));
3409 p.Z = stoi(fn.next(","));
3410 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3412 return meta->getInventory();
3413 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3414 <<"no metadata found"<<std::endl;
3418 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3421 void Server::inventoryModified(InventoryContext *c, std::string id)
3423 if(id == "current_player")
3425 assert(c->current_player);
3427 UpdateCrafting(c->current_player->peer_id);
3428 SendInventory(c->current_player->peer_id);
3433 std::string id0 = fn.next(":");
3435 if(id0 == "nodemeta")
3438 p.X = stoi(fn.next(","));
3439 p.Y = stoi(fn.next(","));
3440 p.Z = stoi(fn.next(","));
3441 v3s16 blockpos = getNodeBlockPos(p);
3443 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3445 meta->inventoryModified();
3447 for(core::map<u16, RemoteClient*>::Iterator
3448 i = m_clients.getIterator();
3449 i.atEnd()==false; i++)
3451 RemoteClient *client = i.getNode()->getValue();
3452 client->SetBlockNotSent(blockpos);
3458 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3461 core::list<PlayerInfo> Server::getPlayerInfo()
3463 DSTACK(__FUNCTION_NAME);
3464 JMutexAutoLock envlock(m_env_mutex);
3465 JMutexAutoLock conlock(m_con_mutex);
3467 core::list<PlayerInfo> list;
3469 core::list<Player*> players = m_env->getPlayers();
3471 core::list<Player*>::Iterator i;
3472 for(i = players.begin();
3473 i != players.end(); i++)
3477 Player *player = *i;
3480 // Copy info from connection to info struct
3481 info.id = player->peer_id;
3482 info.address = m_con.GetPeerAddress(player->peer_id);
3483 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3485 catch(con::PeerNotFoundException &e)
3487 // Set dummy peer info
3489 info.address = Address(0,0,0,0,0);
3493 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3494 info.position = player->getPosition();
3496 list.push_back(info);
3503 void Server::peerAdded(con::Peer *peer)
3505 DSTACK(__FUNCTION_NAME);
3506 infostream<<"Server::peerAdded(): peer->id="
3507 <<peer->id<<std::endl;
3510 c.type = PEER_ADDED;
3511 c.peer_id = peer->id;
3513 m_peer_change_queue.push_back(c);
3516 void Server::deletingPeer(con::Peer *peer, bool timeout)
3518 DSTACK(__FUNCTION_NAME);
3519 infostream<<"Server::deletingPeer(): peer->id="
3520 <<peer->id<<", timeout="<<timeout<<std::endl;
3523 c.type = PEER_REMOVED;
3524 c.peer_id = peer->id;
3525 c.timeout = timeout;
3526 m_peer_change_queue.push_back(c);
3533 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3535 DSTACK(__FUNCTION_NAME);
3536 std::ostringstream os(std::ios_base::binary);
3538 writeU16(os, TOCLIENT_HP);
3542 std::string s = os.str();
3543 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3545 con.Send(peer_id, 0, data, true);
3548 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3549 const std::wstring &reason)
3551 DSTACK(__FUNCTION_NAME);
3552 std::ostringstream os(std::ios_base::binary);
3554 writeU16(os, TOCLIENT_ACCESS_DENIED);
3555 os<<serializeWideString(reason);
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::SendDeathscreen(con::Connection &con, u16 peer_id,
3565 bool set_camera_point_target, v3f camera_point_target)
3567 DSTACK(__FUNCTION_NAME);
3568 std::ostringstream os(std::ios_base::binary);
3570 writeU16(os, TOCLIENT_DEATHSCREEN);
3571 writeU8(os, set_camera_point_target);
3572 writeV3F1000(os, camera_point_target);
3575 std::string s = os.str();
3576 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3578 con.Send(peer_id, 0, data, true);
3582 Non-static send methods
3585 void Server::SendObjectData(float dtime)
3587 DSTACK(__FUNCTION_NAME);
3589 core::map<v3s16, bool> stepped_blocks;
3591 for(core::map<u16, RemoteClient*>::Iterator
3592 i = m_clients.getIterator();
3593 i.atEnd() == false; i++)
3595 u16 peer_id = i.getNode()->getKey();
3596 RemoteClient *client = i.getNode()->getValue();
3597 assert(client->peer_id == peer_id);
3599 if(client->serialization_version == SER_FMT_VER_INVALID)
3602 client->SendObjectData(this, dtime, stepped_blocks);
3606 void Server::SendPlayerInfos()
3608 DSTACK(__FUNCTION_NAME);
3610 //JMutexAutoLock envlock(m_env_mutex);
3612 // Get connected players
3613 core::list<Player*> players = m_env->getPlayers(true);
3615 u32 player_count = players.getSize();
3616 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3618 SharedBuffer<u8> data(datasize);
3619 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3622 core::list<Player*>::Iterator i;
3623 for(i = players.begin();
3624 i != players.end(); i++)
3626 Player *player = *i;
3628 /*infostream<<"Server sending player info for player with "
3629 "peer_id="<<player->peer_id<<std::endl;*/
3631 writeU16(&data[start], player->peer_id);
3632 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3633 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3634 start += 2+PLAYERNAME_SIZE;
3637 //JMutexAutoLock conlock(m_con_mutex);
3640 m_con.SendToAll(0, data, true);
3643 void Server::SendInventory(u16 peer_id)
3645 DSTACK(__FUNCTION_NAME);
3647 Player* player = m_env->getPlayer(peer_id);
3654 std::ostringstream os;
3655 //os.imbue(std::locale("C"));
3657 player->inventory.serialize(os);
3659 std::string s = os.str();
3661 SharedBuffer<u8> data(s.size()+2);
3662 writeU16(&data[0], TOCLIENT_INVENTORY);
3663 memcpy(&data[2], s.c_str(), s.size());
3666 m_con.Send(peer_id, 0, data, true);
3669 std::string getWieldedItemString(const Player *player)
3671 const InventoryItem *item = player->getWieldItem();
3673 return std::string("");
3674 std::ostringstream os(std::ios_base::binary);
3675 item->serialize(os);
3679 void Server::SendWieldedItem(const Player* player)
3681 DSTACK(__FUNCTION_NAME);
3685 std::ostringstream os(std::ios_base::binary);
3687 writeU16(os, TOCLIENT_PLAYERITEM);
3689 writeU16(os, player->peer_id);
3690 os<<serializeString(getWieldedItemString(player));
3693 std::string s = os.str();
3694 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3696 m_con.SendToAll(0, data, true);
3699 void Server::SendPlayerItems()
3701 DSTACK(__FUNCTION_NAME);
3703 std::ostringstream os(std::ios_base::binary);
3704 core::list<Player *> players = m_env->getPlayers(true);
3706 writeU16(os, TOCLIENT_PLAYERITEM);
3707 writeU16(os, players.size());
3708 core::list<Player *>::Iterator i;
3709 for(i = players.begin(); i != players.end(); ++i)
3712 writeU16(os, p->peer_id);
3713 os<<serializeString(getWieldedItemString(p));
3717 std::string s = os.str();
3718 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3720 m_con.SendToAll(0, data, true);
3723 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3725 DSTACK(__FUNCTION_NAME);
3727 std::ostringstream os(std::ios_base::binary);
3731 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3732 os.write((char*)buf, 2);
3735 writeU16(buf, message.size());
3736 os.write((char*)buf, 2);
3739 for(u32 i=0; i<message.size(); i++)
3743 os.write((char*)buf, 2);
3747 std::string s = os.str();
3748 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3750 m_con.Send(peer_id, 0, data, true);
3753 void Server::BroadcastChatMessage(const std::wstring &message)
3755 for(core::map<u16, RemoteClient*>::Iterator
3756 i = m_clients.getIterator();
3757 i.atEnd() == false; i++)
3759 // Get client and check that it is valid
3760 RemoteClient *client = i.getNode()->getValue();
3761 assert(client->peer_id == i.getNode()->getKey());
3762 if(client->serialization_version == SER_FMT_VER_INVALID)
3765 SendChatMessage(client->peer_id, message);
3769 void Server::SendPlayerHP(Player *player)
3771 SendHP(m_con, player->peer_id, player->hp);
3774 void Server::SendMovePlayer(Player *player)
3776 DSTACK(__FUNCTION_NAME);
3777 std::ostringstream os(std::ios_base::binary);
3779 writeU16(os, TOCLIENT_MOVE_PLAYER);
3780 writeV3F1000(os, player->getPosition());
3781 writeF1000(os, player->getPitch());
3782 writeF1000(os, player->getYaw());
3785 v3f pos = player->getPosition();
3786 f32 pitch = player->getPitch();
3787 f32 yaw = player->getYaw();
3788 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3789 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3796 std::string s = os.str();
3797 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3799 m_con.Send(player->peer_id, 0, data, true);
3802 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3803 core::list<u16> *far_players, float far_d_nodes)
3805 float maxd = far_d_nodes*BS;
3806 v3f p_f = intToFloat(p, BS);
3810 SharedBuffer<u8> reply(replysize);
3811 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3812 writeS16(&reply[2], p.X);
3813 writeS16(&reply[4], p.Y);
3814 writeS16(&reply[6], p.Z);
3816 for(core::map<u16, RemoteClient*>::Iterator
3817 i = m_clients.getIterator();
3818 i.atEnd() == false; i++)
3820 // Get client and check that it is valid
3821 RemoteClient *client = i.getNode()->getValue();
3822 assert(client->peer_id == i.getNode()->getKey());
3823 if(client->serialization_version == SER_FMT_VER_INVALID)
3826 // Don't send if it's the same one
3827 if(client->peer_id == ignore_id)
3833 Player *player = m_env->getPlayer(client->peer_id);
3836 // If player is far away, only set modified blocks not sent
3837 v3f player_pos = player->getPosition();
3838 if(player_pos.getDistanceFrom(p_f) > maxd)
3840 far_players->push_back(client->peer_id);
3847 m_con.Send(client->peer_id, 0, reply, true);
3851 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3852 core::list<u16> *far_players, float far_d_nodes)
3854 float maxd = far_d_nodes*BS;
3855 v3f p_f = intToFloat(p, BS);
3857 for(core::map<u16, RemoteClient*>::Iterator
3858 i = m_clients.getIterator();
3859 i.atEnd() == false; i++)
3861 // Get client and check that it is valid
3862 RemoteClient *client = i.getNode()->getValue();
3863 assert(client->peer_id == i.getNode()->getKey());
3864 if(client->serialization_version == SER_FMT_VER_INVALID)
3867 // Don't send if it's the same one
3868 if(client->peer_id == ignore_id)
3874 Player *player = m_env->getPlayer(client->peer_id);
3877 // If player is far away, only set modified blocks not sent
3878 v3f player_pos = player->getPosition();
3879 if(player_pos.getDistanceFrom(p_f) > maxd)
3881 far_players->push_back(client->peer_id);
3888 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3889 SharedBuffer<u8> reply(replysize);
3890 writeU16(&reply[0], TOCLIENT_ADDNODE);
3891 writeS16(&reply[2], p.X);
3892 writeS16(&reply[4], p.Y);
3893 writeS16(&reply[6], p.Z);
3894 n.serialize(&reply[8], client->serialization_version);
3897 m_con.Send(client->peer_id, 0, reply, true);
3901 void Server::setBlockNotSent(v3s16 p)
3903 for(core::map<u16, RemoteClient*>::Iterator
3904 i = m_clients.getIterator();
3905 i.atEnd()==false; i++)
3907 RemoteClient *client = i.getNode()->getValue();
3908 client->SetBlockNotSent(p);
3912 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3914 DSTACK(__FUNCTION_NAME);
3916 v3s16 p = block->getPos();
3920 bool completely_air = true;
3921 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3922 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3923 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3925 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3927 completely_air = false;
3928 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3933 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3935 infostream<<"[completely air] ";
3936 infostream<<std::endl;
3940 Create a packet with the block in the right format
3943 std::ostringstream os(std::ios_base::binary);
3944 block->serialize(os, ver);
3945 std::string s = os.str();
3946 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3948 u32 replysize = 8 + blockdata.getSize();
3949 SharedBuffer<u8> reply(replysize);
3950 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3951 writeS16(&reply[2], p.X);
3952 writeS16(&reply[4], p.Y);
3953 writeS16(&reply[6], p.Z);
3954 memcpy(&reply[8], *blockdata, blockdata.getSize());
3956 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3957 <<": \tpacket size: "<<replysize<<std::endl;*/
3962 m_con.Send(peer_id, 1, reply, true);
3965 void Server::SendBlocks(float dtime)
3967 DSTACK(__FUNCTION_NAME);
3969 JMutexAutoLock envlock(m_env_mutex);
3970 JMutexAutoLock conlock(m_con_mutex);
3972 //TimeTaker timer("Server::SendBlocks");
3974 core::array<PrioritySortedBlockTransfer> queue;
3976 s32 total_sending = 0;
3979 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3981 for(core::map<u16, RemoteClient*>::Iterator
3982 i = m_clients.getIterator();
3983 i.atEnd() == false; i++)
3985 RemoteClient *client = i.getNode()->getValue();
3986 assert(client->peer_id == i.getNode()->getKey());
3988 total_sending += client->SendingCount();
3990 if(client->serialization_version == SER_FMT_VER_INVALID)
3993 client->GetNextBlocks(this, dtime, queue);
3998 // Lowest priority number comes first.
3999 // Lowest is most important.
4002 for(u32 i=0; i<queue.size(); i++)
4004 //TODO: Calculate limit dynamically
4005 if(total_sending >= g_settings->getS32
4006 ("max_simultaneous_block_sends_server_total"))
4009 PrioritySortedBlockTransfer q = queue[i];
4011 MapBlock *block = NULL;
4014 block = m_env->getMap().getBlockNoCreate(q.pos);
4016 catch(InvalidPositionException &e)
4021 RemoteClient *client = getClient(q.peer_id);
4023 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4025 client->SentBlock(q.pos);
4035 void Server::HandlePlayerHP(Player *player, s16 damage)
4037 if(player->hp > damage)
4039 player->hp -= damage;
4040 SendPlayerHP(player);
4044 infostream<<"Server::HandlePlayerHP(): Player "
4045 <<player->getName()<<" dies"<<std::endl;
4049 //TODO: Throw items around
4051 // Handle players that are not connected
4052 if(player->peer_id == PEER_ID_INEXISTENT){
4053 RespawnPlayer(player);
4057 SendPlayerHP(player);
4059 RemoteClient *client = getClient(player->peer_id);
4060 if(client->net_proto_version >= 3)
4062 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4066 RespawnPlayer(player);
4071 void Server::RespawnPlayer(Player *player)
4073 v3f pos = findSpawnPos(m_env->getServerMap());
4074 player->setPosition(pos);
4076 SendMovePlayer(player);
4077 SendPlayerHP(player);
4080 void Server::UpdateCrafting(u16 peer_id)
4082 DSTACK(__FUNCTION_NAME);
4084 Player* player = m_env->getPlayer(peer_id);
4088 Calculate crafting stuff
4090 if(g_settings->getBool("creative_mode") == false)
4092 InventoryList *clist = player->inventory.getList("craft");
4093 InventoryList *rlist = player->inventory.getList("craftresult");
4095 if(rlist && rlist->getUsedSlots() == 0)
4096 player->craftresult_is_preview = true;
4098 if(rlist && player->craftresult_is_preview)
4100 rlist->clearItems();
4102 if(clist && rlist && player->craftresult_is_preview)
4104 InventoryItem *items[9];
4105 for(u16 i=0; i<9; i++)
4107 items[i] = clist->getItem(i);
4110 // Get result of crafting grid
4111 InventoryItem *result = craft_get_result(items);
4113 rlist->addItem(result);
4116 } // if creative_mode == false
4119 RemoteClient* Server::getClient(u16 peer_id)
4121 DSTACK(__FUNCTION_NAME);
4122 //JMutexAutoLock lock(m_con_mutex);
4123 core::map<u16, RemoteClient*>::Node *n;
4124 n = m_clients.find(peer_id);
4125 // A client should exist for all peers
4127 return n->getValue();
4130 std::wstring Server::getStatusString()
4132 std::wostringstream os(std::ios_base::binary);
4135 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4137 os<<L", uptime="<<m_uptime.get();
4138 // Information about clients
4140 for(core::map<u16, RemoteClient*>::Iterator
4141 i = m_clients.getIterator();
4142 i.atEnd() == false; i++)
4144 // Get client and check that it is valid
4145 RemoteClient *client = i.getNode()->getValue();
4146 assert(client->peer_id == i.getNode()->getKey());
4147 if(client->serialization_version == SER_FMT_VER_INVALID)
4150 Player *player = m_env->getPlayer(client->peer_id);
4151 // Get name of player
4152 std::wstring name = L"unknown";
4154 name = narrow_to_wide(player->getName());
4155 // Add name to information string
4159 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4160 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4161 if(g_settings->get("motd") != "")
4162 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4166 // Saves g_settings to configpath given at initialization
4167 void Server::saveConfig()
4169 if(m_configpath != "")
4170 g_settings->updateConfigFile(m_configpath.c_str());
4173 void Server::notifyPlayer(const char *name, const std::wstring msg)
4175 Player *player = m_env->getPlayer(name);
4178 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4181 void Server::notifyPlayers(const std::wstring msg)
4183 BroadcastChatMessage(msg);
4186 v3f findSpawnPos(ServerMap &map)
4188 //return v3f(50,50,50)*BS;
4193 nodepos = v2s16(0,0);
4198 // Try to find a good place a few times
4199 for(s32 i=0; i<1000; i++)
4202 // We're going to try to throw the player to this position
4203 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4204 -range + (myrand()%(range*2)));
4205 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4206 // Get ground height at point (fallbacks to heightmap function)
4207 s16 groundheight = map.findGroundLevel(nodepos2d);
4208 // Don't go underwater
4209 if(groundheight < WATER_LEVEL)
4211 //infostream<<"-> Underwater"<<std::endl;
4214 // Don't go to high places
4215 if(groundheight > WATER_LEVEL + 4)
4217 //infostream<<"-> Underwater"<<std::endl;
4221 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4222 bool is_good = false;
4224 for(s32 i=0; i<10; i++){
4225 v3s16 blockpos = getNodeBlockPos(nodepos);
4226 map.emergeBlock(blockpos, true);
4227 MapNode n = map.getNodeNoEx(nodepos);
4228 if(n.getContent() == CONTENT_AIR){
4239 // Found a good place
4240 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4246 return intToFloat(nodepos, BS);
4249 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4252 Try to get an existing player
4254 Player *player = m_env->getPlayer(name);
4257 // If player is already connected, cancel
4258 if(player->peer_id != 0)
4260 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4265 player->peer_id = peer_id;
4267 // Reset inventory to creative if in creative mode
4268 if(g_settings->getBool("creative_mode"))
4270 // Warning: double code below
4271 // Backup actual inventory
4272 player->inventory_backup = new Inventory();
4273 *(player->inventory_backup) = player->inventory;
4274 // Set creative inventory
4275 craft_set_creative_inventory(player);
4282 If player with the wanted peer_id already exists, cancel.
4284 if(m_env->getPlayer(peer_id) != NULL)
4286 infostream<<"emergePlayer(): Player with wrong name but same"
4287 " peer_id already exists"<<std::endl;
4295 // Add authentication stuff
4296 m_authmanager.add(name);
4297 m_authmanager.setPassword(name, password);
4298 m_authmanager.setPrivs(name,
4299 stringToPrivs(g_settings->get("default_privs")));
4305 infostream<<"Server: Finding spawn place for player \""
4306 <<name<<"\""<<std::endl;
4308 v3f pos = findSpawnPos(m_env->getServerMap());
4310 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4313 Add player to environment
4316 m_env->addPlayer(player);
4319 Add stuff to inventory
4322 if(g_settings->getBool("creative_mode"))
4324 // Warning: double code above
4325 // Backup actual inventory
4326 player->inventory_backup = new Inventory();
4327 *(player->inventory_backup) = player->inventory;
4328 // Set creative inventory
4329 craft_set_creative_inventory(player);
4331 else if(g_settings->getBool("give_initial_stuff"))
4333 craft_give_initial_stuff(player);
4338 } // create new player
4341 void Server::handlePeerChange(PeerChange &c)
4343 JMutexAutoLock envlock(m_env_mutex);
4344 JMutexAutoLock conlock(m_con_mutex);
4346 if(c.type == PEER_ADDED)
4353 core::map<u16, RemoteClient*>::Node *n;
4354 n = m_clients.find(c.peer_id);
4355 // The client shouldn't already exist
4359 RemoteClient *client = new RemoteClient();
4360 client->peer_id = c.peer_id;
4361 m_clients.insert(client->peer_id, client);
4364 else if(c.type == PEER_REMOVED)
4371 core::map<u16, RemoteClient*>::Node *n;
4372 n = m_clients.find(c.peer_id);
4373 // The client should exist
4377 Mark objects to be not known by the client
4379 RemoteClient *client = n->getValue();
4381 for(core::map<u16, bool>::Iterator
4382 i = client->m_known_objects.getIterator();
4383 i.atEnd()==false; i++)
4386 u16 id = i.getNode()->getKey();
4387 ServerActiveObject* obj = m_env->getActiveObject(id);
4389 if(obj && obj->m_known_by_count > 0)
4390 obj->m_known_by_count--;
4393 // Collect information about leaving in chat
4394 std::wstring message;
4396 Player *player = m_env->getPlayer(c.peer_id);
4399 std::wstring name = narrow_to_wide(player->getName());
4402 message += L" left game";
4404 message += L" (timed out)";
4410 m_env->removePlayer(c.peer_id);
4413 // Set player client disconnected
4415 Player *player = m_env->getPlayer(c.peer_id);
4417 player->peer_id = 0;
4424 std::ostringstream os(std::ios_base::binary);
4425 for(core::map<u16, RemoteClient*>::Iterator
4426 i = m_clients.getIterator();
4427 i.atEnd() == false; i++)
4429 RemoteClient *client = i.getNode()->getValue();
4430 assert(client->peer_id == i.getNode()->getKey());
4431 if(client->serialization_version == SER_FMT_VER_INVALID)
4434 Player *player = m_env->getPlayer(client->peer_id);
4437 // Get name of player
4438 os<<player->getName()<<" ";
4441 actionstream<<player->getName()<<" "
4442 <<(c.timeout?"times out.":"leaves game.")
4443 <<" List of players: "
4444 <<os.str()<<std::endl;
4449 delete m_clients[c.peer_id];
4450 m_clients.remove(c.peer_id);
4452 // Send player info to all remaining clients
4455 // Send leave chat message to all remaining clients
4456 BroadcastChatMessage(message);
4465 void Server::handlePeerChanges()
4467 while(m_peer_change_queue.size() > 0)
4469 PeerChange c = m_peer_change_queue.pop_front();
4471 infostream<<"Server: Handling peer change: "
4472 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4475 handlePeerChange(c);
4479 u64 Server::getPlayerPrivs(Player *player)
4483 std::string playername = player->getName();
4484 // Local player gets all privileges regardless of
4485 // what's set on their account.
4486 if(g_settings->get("name") == playername)
4492 return getPlayerAuthPrivs(playername);
4496 void dedicated_server_loop(Server &server, bool &kill)
4498 DSTACK(__FUNCTION_NAME);
4500 infostream<<DTIME<<std::endl;
4501 infostream<<"========================"<<std::endl;
4502 infostream<<"Running dedicated server"<<std::endl;
4503 infostream<<"========================"<<std::endl;
4504 infostream<<std::endl;
4506 IntervalLimiter m_profiler_interval;
4510 // This is kind of a hack but can be done like this
4511 // because server.step() is very light
4513 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4518 if(server.getShutdownRequested() || kill)
4520 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4527 float profiler_print_interval =
4528 g_settings->getFloat("profiler_print_interval");
4529 if(profiler_print_interval != 0)
4531 if(m_profiler_interval.step(0.030, profiler_print_interval))
4533 infostream<<"Profiler:"<<std::endl;
4534 g_profiler->print(infostream);
4535 g_profiler->clear();
4542 static int counter = 0;
4548 core::list<PlayerInfo> list = server.getPlayerInfo();
4549 core::list<PlayerInfo>::Iterator i;
4550 static u32 sum_old = 0;
4551 u32 sum = PIChecksum(list);
4554 infostream<<DTIME<<"Player info:"<<std::endl;
4555 for(i=list.begin(); i!=list.end(); i++)
4557 i->PrintLine(&infostream);