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"
42 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
44 class MapEditEventIgnorer
47 MapEditEventIgnorer(bool *flag):
56 ~MapEditEventIgnorer()
69 void * ServerThread::Thread()
73 DSTACK(__FUNCTION_NAME);
75 BEGIN_DEBUG_EXCEPTION_HANDLER
80 //TimeTaker timer("AsyncRunStep() + Receive()");
83 //TimeTaker timer("AsyncRunStep()");
84 m_server->AsyncRunStep();
87 //dout_server<<"Running m_server->Receive()"<<std::endl;
90 catch(con::NoIncomingDataException &e)
93 catch(con::PeerNotFoundException &e)
95 dout_server<<"Server: PeerNotFoundException"<<std::endl;
99 END_DEBUG_EXCEPTION_HANDLER
104 void * EmergeThread::Thread()
108 DSTACK(__FUNCTION_NAME);
110 BEGIN_DEBUG_EXCEPTION_HANDLER
112 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
115 Get block info from queue, emerge them and send them
118 After queue is empty, exit.
122 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
126 SharedPtr<QueuedBlockEmerge> q(qptr);
132 Do not generate over-limit
134 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
138 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
139 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
142 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
144 //TimeTaker timer("block emerge");
147 Try to emerge it from somewhere.
149 If it is only wanted as optional, only loading from disk
154 Check if any peer wants it as non-optional. In that case it
157 Also decrement the emerge queue count in clients.
160 bool only_from_disk = true;
163 core::map<u16, u8>::Iterator i;
164 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
166 //u16 peer_id = i.getNode()->getKey();
169 u8 flags = i.getNode()->getValue();
170 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
171 only_from_disk = false;
176 if(enable_mapgen_debug_info)
177 dstream<<"EmergeThread: p="
178 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
179 <<"only_from_disk="<<only_from_disk<<std::endl;
181 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
183 //core::map<v3s16, MapBlock*> changed_blocks;
184 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
186 MapBlock *block = NULL;
187 bool got_block = true;
188 core::map<v3s16, MapBlock*> modified_blocks;
191 Fetch block from map or generate a single block
194 JMutexAutoLock envlock(m_server->m_env_mutex);
196 // Load sector if it isn't loaded
197 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
198 //map.loadSectorFull(p2d);
199 map.loadSectorMeta(p2d);
201 block = map.getBlockNoCreateNoEx(p);
202 if(!block || block->isDummy() || !block->isGenerated())
204 if(enable_mapgen_debug_info)
205 dstream<<"EmergeThread: not in memory, loading"<<std::endl;
207 // Get, load or create sector
208 /*ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);*/
211 // Load/generate block
213 /*block = map.emergeBlock(p, sector, changed_blocks,
214 lighting_invalidated_blocks);*/
216 block = map.loadBlock(p);
218 if(only_from_disk == false)
220 if(block == NULL || block->isGenerated() == false)
222 if(enable_mapgen_debug_info)
223 dstream<<"EmergeThread: generating"<<std::endl;
224 block = map.generateBlock(p, modified_blocks);
228 if(enable_mapgen_debug_info)
229 dstream<<"EmergeThread: ended up with: "
230 <<analyze_block(block)<<std::endl;
239 Ignore map edit events, they will not need to be
240 sent to anybody because the block hasn't been sent
243 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
245 // Activate objects and stuff
246 m_server->m_env.activateBlock(block, 3600);
251 /*if(block->getLightingExpired()){
252 lighting_invalidated_blocks[block->getPos()] = block;
256 // TODO: Some additional checking and lighting updating,
261 JMutexAutoLock envlock(m_server->m_env_mutex);
266 Collect a list of blocks that have been modified in
267 addition to the fetched one.
271 if(lighting_invalidated_blocks.size() > 0)
273 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
274 <<" blocks"<<std::endl;*/
276 // 50-100ms for single block generation
277 //TimeTaker timer("** EmergeThread updateLighting");
279 // Update lighting without locking the environment mutex,
280 // add modified blocks to changed blocks
281 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
284 // Add all from changed_blocks to modified_blocks
285 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
286 i.atEnd() == false; i++)
288 MapBlock *block = i.getNode()->getValue();
289 modified_blocks.insert(block->getPos(), block);
293 // If we got no block, there should be no invalidated blocks
296 //assert(lighting_invalidated_blocks.size() == 0);
302 Set sent status of modified blocks on clients
305 // NOTE: Server's clients are also behind the connection mutex
306 JMutexAutoLock lock(m_server->m_con_mutex);
309 Add the originally fetched block to the modified list
313 modified_blocks.insert(p, block);
317 Set the modified blocks unsent for all the clients
320 for(core::map<u16, RemoteClient*>::Iterator
321 i = m_server->m_clients.getIterator();
322 i.atEnd() == false; i++)
324 RemoteClient *client = i.getNode()->getValue();
326 if(modified_blocks.size() > 0)
328 // Remove block from sent history
329 client->SetBlocksNotSent(modified_blocks);
335 END_DEBUG_EXCEPTION_HANDLER
340 void RemoteClient::GetNextBlocks(Server *server, float dtime,
341 core::array<PrioritySortedBlockTransfer> &dest)
343 DSTACK(__FUNCTION_NAME);
346 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
349 m_nothing_to_send_pause_timer -= dtime;
351 if(m_nothing_to_send_pause_timer >= 0)
354 m_nearest_unsent_reset_timer = 0;
358 // Won't send anything if already sending
359 if(m_blocks_sending.size() >= g_settings->getU16
360 ("max_simultaneous_block_sends_per_client"))
362 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
366 //TimeTaker timer("RemoteClient::GetNextBlocks");
368 Player *player = server->m_env.getPlayer(peer_id);
370 assert(player != NULL);
372 v3f playerpos = player->getPosition();
373 v3f playerspeed = player->getSpeed();
374 v3f playerspeeddir(0,0,0);
375 if(playerspeed.getLength() > 1.0*BS)
376 playerspeeddir = playerspeed / playerspeed.getLength();
377 // Predict to next block
378 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
380 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
382 v3s16 center = getNodeBlockPos(center_nodepos);
384 // Camera position and direction
385 v3f camera_pos = player->getEyePosition();
386 v3f camera_dir = v3f(0,0,1);
387 camera_dir.rotateYZBy(player->getPitch());
388 camera_dir.rotateXZBy(player->getYaw());
390 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
391 <<camera_dir.Z<<")"<<std::endl;*/
394 Get the starting value of the block finder radius.
397 if(m_last_center != center)
399 m_nearest_unsent_d = 0;
400 m_last_center = center;
403 /*dstream<<"m_nearest_unsent_reset_timer="
404 <<m_nearest_unsent_reset_timer<<std::endl;*/
406 // This has to be incremented only when the nothing to send pause
408 m_nearest_unsent_reset_timer += dtime;
410 // Reset periodically to avoid possible bugs or other mishaps
411 if(m_nearest_unsent_reset_timer > 10.0)
413 m_nearest_unsent_reset_timer = 0;
414 m_nearest_unsent_d = 0;
415 /*dstream<<"Resetting m_nearest_unsent_d for "
416 <<server->getPlayerName(peer_id)<<std::endl;*/
419 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
420 s16 d_start = m_nearest_unsent_d;
422 //dstream<<"d_start="<<d_start<<std::endl;
424 u16 max_simul_sends_setting = g_settings->getU16
425 ("max_simultaneous_block_sends_per_client");
426 u16 max_simul_sends_usually = max_simul_sends_setting;
429 Check the time from last addNode/removeNode.
431 Decrease send rate if player is building stuff.
433 m_time_from_building += dtime;
434 if(m_time_from_building < g_settings->getFloat(
435 "full_block_send_enable_min_time_from_building"))
437 max_simul_sends_usually
438 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
442 Number of blocks sending + number of blocks selected for sending
444 u32 num_blocks_selected = m_blocks_sending.size();
447 next time d will be continued from the d from which the nearest
448 unsent block was found this time.
450 This is because not necessarily any of the blocks found this
451 time are actually sent.
453 s32 new_nearest_unsent_d = -1;
455 s16 d_max = g_settings->getS16("max_block_send_distance");
456 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
458 // Don't loop very much at a time
459 if(d_max > d_start+1)
461 /*if(d_max_gen > d_start+2)
462 d_max_gen = d_start+2;*/
464 //dstream<<"Starting from "<<d_start<<std::endl;
466 bool sending_something = false;
468 bool no_blocks_found_for_sending = true;
470 bool queue_is_full = false;
473 for(d = d_start; d <= d_max; d++)
475 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
478 If m_nearest_unsent_d was changed by the EmergeThread
479 (it can change it to 0 through SetBlockNotSent),
481 Else update m_nearest_unsent_d
483 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
485 d = m_nearest_unsent_d;
486 last_nearest_unsent_d = m_nearest_unsent_d;
490 Get the border/face dot coordinates of a "d-radiused"
493 core::list<v3s16> list;
494 getFacePositions(list, d);
496 core::list<v3s16>::Iterator li;
497 for(li=list.begin(); li!=list.end(); li++)
499 v3s16 p = *li + center;
503 - Don't allow too many simultaneous transfers
504 - EXCEPT when the blocks are very close
506 Also, don't send blocks that are already flying.
509 // Start with the usual maximum
510 u16 max_simul_dynamic = max_simul_sends_usually;
512 // If block is very close, allow full maximum
513 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
514 max_simul_dynamic = max_simul_sends_setting;
516 // Don't select too many blocks for sending
517 if(num_blocks_selected >= max_simul_dynamic)
519 queue_is_full = true;
520 goto queue_full_break;
523 // Don't send blocks that are currently being transferred
524 if(m_blocks_sending.find(p) != NULL)
530 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
534 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
535 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
538 // If this is true, inexistent block will be made from scratch
539 bool generate = d <= d_max_gen;
542 /*// Limit the generating area vertically to 2/3
543 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
546 // Limit the send area vertically to 2/3
547 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
553 If block is far away, don't generate it unless it is
559 // Block center y in nodes
560 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
561 // Don't generate if it's very high or very low
562 if(y < -64 || y > 64)
566 v2s16 p2d_nodes_center(
570 // Get ground height in nodes
571 s16 gh = server->m_env.getServerMap().findGroundLevel(
574 // If differs a lot, don't generate
575 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
577 // Actually, don't even send it
583 //dstream<<"d="<<d<<std::endl;
586 Don't generate or send if not in sight
587 FIXME This only works if the client uses a small enough
588 FOV setting. The default of 72 degrees is fine.
591 float camera_fov = (72.0*PI/180) * 4./3.;
592 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
598 Don't send already sent blocks
601 if(m_blocks_sent.find(p) != NULL)
608 Check if map has this block
610 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
612 bool surely_not_found_on_disk = false;
613 bool block_is_invalid = false;
616 // Reset usage timer, this block will be of use in the future.
617 block->resetUsageTimer();
619 // Block is dummy if data doesn't exist.
620 // It means it has been not found from disk and not generated
623 surely_not_found_on_disk = true;
626 // Block is valid if lighting is up-to-date and data exists
627 if(block->isValid() == false)
629 block_is_invalid = true;
632 /*if(block->isFullyGenerated() == false)
634 block_is_invalid = true;
639 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
640 v2s16 chunkpos = map->sector_to_chunk(p2d);
641 if(map->chunkNonVolatile(chunkpos) == false)
642 block_is_invalid = true;
644 if(block->isGenerated() == false)
645 block_is_invalid = true;
648 If block is not close, don't send it unless it is near
651 Block is near ground level if night-time mesh
652 differs from day-time mesh.
656 if(block->dayNightDiffed() == false)
663 If block has been marked to not exist on disk (dummy)
664 and generating new ones is not wanted, skip block.
666 if(generate == false && surely_not_found_on_disk == true)
673 Record the lowest d from which a block has been
674 found being not sent and possibly to exist
676 if(no_blocks_found_for_sending)
679 new_nearest_unsent_d = d;
682 no_blocks_found_for_sending = false;
685 Add inexistent block to emerge queue.
687 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
689 //TODO: Get value from somewhere
690 // Allow only one block in emerge queue
691 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
692 // Allow two blocks in queue per client
693 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
695 //dstream<<"Adding block to emerge queue"<<std::endl;
697 // Add it to the emerge queue and trigger the thread
700 if(generate == false)
701 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
703 server->m_emerge_queue.addBlock(peer_id, p, flags);
704 server->m_emergethread.trigger();
712 Add block to send queue
715 PrioritySortedBlockTransfer q((float)d, p, peer_id);
719 num_blocks_selected += 1;
720 sending_something = true;
725 //dstream<<"Stopped at "<<d<<std::endl;
727 if(no_blocks_found_for_sending)
729 if(queue_is_full == false)
730 new_nearest_unsent_d = d;
733 if(new_nearest_unsent_d != -1)
734 m_nearest_unsent_d = new_nearest_unsent_d;
736 if(sending_something == false)
738 m_nothing_to_send_counter++;
739 if((s16)m_nothing_to_send_counter >=
740 g_settings->getS16("max_block_send_distance"))
742 // Pause time in seconds
743 m_nothing_to_send_pause_timer = 1.0;
744 /*dstream<<"nothing to send to "
745 <<server->getPlayerName(peer_id)
746 <<" (d="<<d<<")"<<std::endl;*/
751 m_nothing_to_send_counter = 0;
754 /*timer_result = timer.stop(true);
755 if(timer_result != 0)
756 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
759 void RemoteClient::SendObjectData(
762 core::map<v3s16, bool> &stepped_blocks
765 DSTACK(__FUNCTION_NAME);
767 // Can't send anything without knowing version
768 if(serialization_version == SER_FMT_VER_INVALID)
770 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
776 Send a TOCLIENT_OBJECTDATA packet.
780 u16 number of player positions
792 std::ostringstream os(std::ios_base::binary);
796 writeU16(buf, TOCLIENT_OBJECTDATA);
797 os.write((char*)buf, 2);
800 Get and write player data
803 // Get connected players
804 core::list<Player*> players = server->m_env.getPlayers(true);
806 // Write player count
807 u16 playercount = players.size();
808 writeU16(buf, playercount);
809 os.write((char*)buf, 2);
811 core::list<Player*>::Iterator i;
812 for(i = players.begin();
813 i != players.end(); i++)
817 v3f pf = player->getPosition();
818 v3f sf = player->getSpeed();
820 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
821 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
822 s32 pitch_i (player->getPitch() * 100);
823 s32 yaw_i (player->getYaw() * 100);
825 writeU16(buf, player->peer_id);
826 os.write((char*)buf, 2);
827 writeV3S32(buf, position_i);
828 os.write((char*)buf, 12);
829 writeV3S32(buf, speed_i);
830 os.write((char*)buf, 12);
831 writeS32(buf, pitch_i);
832 os.write((char*)buf, 4);
833 writeS32(buf, yaw_i);
834 os.write((char*)buf, 4);
838 Get and write object data
844 For making players to be able to build to their nearby
845 environment (building is not possible on blocks that are not
848 - Add blocks to emerge queue if they are not found
850 SUGGESTION: These could be ignored from the backside of the player
853 Player *player = server->m_env.getPlayer(peer_id);
857 v3f playerpos = player->getPosition();
858 v3f playerspeed = player->getSpeed();
860 v3s16 center_nodepos = floatToInt(playerpos, BS);
861 v3s16 center = getNodeBlockPos(center_nodepos);
863 s16 d_max = g_settings->getS16("active_object_range");
865 // Number of blocks whose objects were written to bos
868 std::ostringstream bos(std::ios_base::binary);
870 for(s16 d = 0; d <= d_max; d++)
872 core::list<v3s16> list;
873 getFacePositions(list, d);
875 core::list<v3s16>::Iterator li;
876 for(li=list.begin(); li!=list.end(); li++)
878 v3s16 p = *li + center;
881 Ignore blocks that haven't been sent to the client
884 if(m_blocks_sent.find(p) == NULL)
888 // Try stepping block and add it to a send queue
893 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
896 Step block if not in stepped_blocks and add to stepped_blocks.
898 if(stepped_blocks.find(p) == NULL)
900 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
901 stepped_blocks.insert(p, true);
902 //block->setChangedFlag();
905 // Skip block if there are no objects
906 if(block->getObjectCount() == 0)
915 bos.write((char*)buf, 6);
918 //block->serializeObjects(bos, serialization_version); // DEPRECATED
925 Stop collecting objects if data is already too big
927 // Sum of player and object data sizes
928 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
929 // break out if data too big
930 if(sum > MAX_OBJECTDATA_SIZE)
932 goto skip_subsequent;
936 catch(InvalidPositionException &e)
939 // Add it to the emerge queue and trigger the thread.
940 // Fetch the block only if it is on disk.
942 // Grab and increment counter
943 /*SharedPtr<JMutexAutoLock> lock
944 (m_num_blocks_in_emerge_queue.getLock());
945 m_num_blocks_in_emerge_queue.m_value++;*/
947 // Add to queue as an anonymous fetch from disk
948 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
949 server->m_emerge_queue.addBlock(0, p, flags);
950 server->m_emergethread.trigger();
958 writeU16(buf, blockcount);
959 os.write((char*)buf, 2);
961 // Write block objects
968 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
971 std::string s = os.str();
972 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
973 // Send as unreliable
974 server->m_con.Send(peer_id, 0, data, false);
977 void RemoteClient::GotBlock(v3s16 p)
979 if(m_blocks_sending.find(p) != NULL)
980 m_blocks_sending.remove(p);
983 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
984 " m_blocks_sending"<<std::endl;*/
985 m_excess_gotblocks++;
987 m_blocks_sent.insert(p, true);
990 void RemoteClient::SentBlock(v3s16 p)
992 if(m_blocks_sending.find(p) == NULL)
993 m_blocks_sending.insert(p, 0.0);
995 dstream<<"RemoteClient::SentBlock(): Sent block"
996 " already in m_blocks_sending"<<std::endl;
999 void RemoteClient::SetBlockNotSent(v3s16 p)
1001 m_nearest_unsent_d = 0;
1003 if(m_blocks_sending.find(p) != NULL)
1004 m_blocks_sending.remove(p);
1005 if(m_blocks_sent.find(p) != NULL)
1006 m_blocks_sent.remove(p);
1009 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1011 m_nearest_unsent_d = 0;
1013 for(core::map<v3s16, MapBlock*>::Iterator
1014 i = blocks.getIterator();
1015 i.atEnd()==false; i++)
1017 v3s16 p = i.getNode()->getKey();
1019 if(m_blocks_sending.find(p) != NULL)
1020 m_blocks_sending.remove(p);
1021 if(m_blocks_sent.find(p) != NULL)
1022 m_blocks_sent.remove(p);
1030 PlayerInfo::PlayerInfo()
1036 void PlayerInfo::PrintLine(std::ostream *s)
1039 (*s)<<"\""<<name<<"\" ("
1040 <<(position.X/10)<<","<<(position.Y/10)
1041 <<","<<(position.Z/10)<<") ";
1043 (*s)<<" avg_rtt="<<avg_rtt;
1047 u32 PIChecksum(core::list<PlayerInfo> &l)
1049 core::list<PlayerInfo>::Iterator i;
1052 for(i=l.begin(); i!=l.end(); i++)
1054 checksum += a * (i->id+1);
1055 checksum ^= 0x435aafcd;
1066 std::string mapsavedir,
1067 std::string configpath
1069 m_env(new ServerMap(mapsavedir), this),
1070 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1071 m_authmanager(mapsavedir+"/auth.txt"),
1072 m_banmanager(mapsavedir+"/ipban.txt"),
1074 m_emergethread(this),
1076 m_time_of_day_send_timer(0),
1078 m_mapsavedir(mapsavedir),
1079 m_configpath(configpath),
1080 m_shutdown_requested(false),
1081 m_ignore_map_edit_events(false),
1082 m_ignore_map_edit_events_peer_id(0)
1084 m_liquid_transform_timer = 0.0;
1085 m_print_info_timer = 0.0;
1086 m_objectdata_timer = 0.0;
1087 m_emergethread_trigger_timer = 0.0;
1088 m_savemap_timer = 0.0;
1092 m_step_dtime_mutex.Init();
1095 // Register us to receive map edit events
1096 m_env.getMap().addEventReceiver(this);
1098 // If file exists, load environment metadata
1099 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1101 dstream<<"Server: Loading environment metadata"<<std::endl;
1102 m_env.loadMeta(m_mapsavedir);
1106 dstream<<"Server: Loading players"<<std::endl;
1107 m_env.deSerializePlayers(m_mapsavedir);
1112 dstream<<"Server::~Server()"<<std::endl;
1115 Send shutdown message
1118 JMutexAutoLock conlock(m_con_mutex);
1120 std::wstring line = L"*** Server shutting down";
1123 Send the message to clients
1125 for(core::map<u16, RemoteClient*>::Iterator
1126 i = m_clients.getIterator();
1127 i.atEnd() == false; i++)
1129 // Get client and check that it is valid
1130 RemoteClient *client = i.getNode()->getValue();
1131 assert(client->peer_id == i.getNode()->getKey());
1132 if(client->serialization_version == SER_FMT_VER_INVALID)
1136 SendChatMessage(client->peer_id, line);
1138 catch(con::PeerNotFoundException &e)
1146 dstream<<"Server: Saving players"<<std::endl;
1147 m_env.serializePlayers(m_mapsavedir);
1150 Save environment metadata
1152 dstream<<"Server: Saving environment metadata"<<std::endl;
1153 m_env.saveMeta(m_mapsavedir);
1164 JMutexAutoLock clientslock(m_con_mutex);
1166 for(core::map<u16, RemoteClient*>::Iterator
1167 i = m_clients.getIterator();
1168 i.atEnd() == false; i++)
1171 // NOTE: These are removed by env destructor
1173 u16 peer_id = i.getNode()->getKey();
1174 JMutexAutoLock envlock(m_env_mutex);
1175 m_env.removePlayer(peer_id);
1179 delete i.getNode()->getValue();
1184 void Server::start(unsigned short port)
1186 DSTACK(__FUNCTION_NAME);
1187 // Stop thread if already running
1190 // Initialize connection
1191 m_con.setTimeoutMs(30);
1195 m_thread.setRun(true);
1198 dout_server<<"Server: Started on port "<<port<<std::endl;
1203 DSTACK(__FUNCTION_NAME);
1205 // Stop threads (set run=false first so both start stopping)
1206 m_thread.setRun(false);
1207 m_emergethread.setRun(false);
1209 m_emergethread.stop();
1211 dout_server<<"Server: Threads stopped"<<std::endl;
1214 void Server::step(float dtime)
1216 DSTACK(__FUNCTION_NAME);
1221 JMutexAutoLock lock(m_step_dtime_mutex);
1222 m_step_dtime += dtime;
1226 void Server::AsyncRunStep()
1228 DSTACK(__FUNCTION_NAME);
1232 JMutexAutoLock lock1(m_step_dtime_mutex);
1233 dtime = m_step_dtime;
1237 ScopeProfiler sp(g_profiler, "Server: selecting and sending "
1238 "blocks to clients");
1239 // Send blocks to clients
1246 //dstream<<"Server steps "<<dtime<<std::endl;
1247 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1250 JMutexAutoLock lock1(m_step_dtime_mutex);
1251 m_step_dtime -= dtime;
1258 m_uptime.set(m_uptime.get() + dtime);
1262 // Process connection's timeouts
1263 JMutexAutoLock lock2(m_con_mutex);
1264 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1265 m_con.RunTimeouts(dtime);
1269 // This has to be called so that the client list gets synced
1270 // with the peer list of the connection
1271 ScopeProfiler sp(g_profiler, "Server: peer change handling");
1272 handlePeerChanges();
1276 Update m_time_of_day and overall game time
1279 JMutexAutoLock envlock(m_env_mutex);
1281 m_time_counter += dtime;
1282 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1283 u32 units = (u32)(m_time_counter*speed);
1284 m_time_counter -= (f32)units / speed;
1286 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1288 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1291 Send to clients at constant intervals
1294 m_time_of_day_send_timer -= dtime;
1295 if(m_time_of_day_send_timer < 0.0)
1297 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1299 //JMutexAutoLock envlock(m_env_mutex);
1300 JMutexAutoLock conlock(m_con_mutex);
1302 for(core::map<u16, RemoteClient*>::Iterator
1303 i = m_clients.getIterator();
1304 i.atEnd() == false; i++)
1306 RemoteClient *client = i.getNode()->getValue();
1307 //Player *player = m_env.getPlayer(client->peer_id);
1309 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1310 m_env.getTimeOfDay());
1312 m_con.Send(client->peer_id, 0, data, true);
1318 JMutexAutoLock lock(m_env_mutex);
1320 ScopeProfiler sp(g_profiler, "Server: environment step");
1324 const float map_timer_and_unload_dtime = 5.15;
1325 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1327 JMutexAutoLock lock(m_env_mutex);
1328 // Run Map's timers and unload unused data
1329 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1330 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1331 g_settings->getFloat("server_unload_unused_data_timeout"));
1341 m_liquid_transform_timer += dtime;
1342 if(m_liquid_transform_timer >= 1.00)
1344 m_liquid_transform_timer -= 1.00;
1346 JMutexAutoLock lock(m_env_mutex);
1348 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1350 core::map<v3s16, MapBlock*> modified_blocks;
1351 m_env.getMap().transformLiquids(modified_blocks);
1356 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1357 ServerMap &map = ((ServerMap&)m_env.getMap());
1358 map.updateLighting(modified_blocks, lighting_modified_blocks);
1360 // Add blocks modified by lighting to modified_blocks
1361 for(core::map<v3s16, MapBlock*>::Iterator
1362 i = lighting_modified_blocks.getIterator();
1363 i.atEnd() == false; i++)
1365 MapBlock *block = i.getNode()->getValue();
1366 modified_blocks.insert(block->getPos(), block);
1370 Set the modified blocks unsent for all the clients
1373 JMutexAutoLock lock2(m_con_mutex);
1375 for(core::map<u16, RemoteClient*>::Iterator
1376 i = m_clients.getIterator();
1377 i.atEnd() == false; i++)
1379 RemoteClient *client = i.getNode()->getValue();
1381 if(modified_blocks.size() > 0)
1383 // Remove block from sent history
1384 client->SetBlocksNotSent(modified_blocks);
1389 // Periodically print some info
1391 float &counter = m_print_info_timer;
1397 JMutexAutoLock lock2(m_con_mutex);
1399 for(core::map<u16, RemoteClient*>::Iterator
1400 i = m_clients.getIterator();
1401 i.atEnd() == false; i++)
1403 //u16 peer_id = i.getNode()->getKey();
1404 RemoteClient *client = i.getNode()->getValue();
1405 Player *player = m_env.getPlayer(client->peer_id);
1408 std::cout<<player->getName()<<"\t";
1409 client->PrintInfo(std::cout);
1414 //if(g_settings->getBool("enable_experimental"))
1418 Check added and deleted active objects
1421 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1422 JMutexAutoLock envlock(m_env_mutex);
1423 JMutexAutoLock conlock(m_con_mutex);
1425 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objects");
1427 // Radius inside which objects are active
1430 for(core::map<u16, RemoteClient*>::Iterator
1431 i = m_clients.getIterator();
1432 i.atEnd() == false; i++)
1434 RemoteClient *client = i.getNode()->getValue();
1435 Player *player = m_env.getPlayer(client->peer_id);
1438 // This can happen if the client timeouts somehow
1439 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1441 <<" has no associated player"<<std::endl;*/
1444 v3s16 pos = floatToInt(player->getPosition(), BS);
1446 core::map<u16, bool> removed_objects;
1447 core::map<u16, bool> added_objects;
1448 m_env.getRemovedActiveObjects(pos, radius,
1449 client->m_known_objects, removed_objects);
1450 m_env.getAddedActiveObjects(pos, radius,
1451 client->m_known_objects, added_objects);
1453 // Ignore if nothing happened
1454 if(removed_objects.size() == 0 && added_objects.size() == 0)
1456 //dstream<<"INFO: active objects: none changed"<<std::endl;
1460 std::string data_buffer;
1464 // Handle removed objects
1465 writeU16((u8*)buf, removed_objects.size());
1466 data_buffer.append(buf, 2);
1467 for(core::map<u16, bool>::Iterator
1468 i = removed_objects.getIterator();
1469 i.atEnd()==false; i++)
1472 u16 id = i.getNode()->getKey();
1473 ServerActiveObject* obj = m_env.getActiveObject(id);
1475 // Add to data buffer for sending
1476 writeU16((u8*)buf, i.getNode()->getKey());
1477 data_buffer.append(buf, 2);
1479 // Remove from known objects
1480 client->m_known_objects.remove(i.getNode()->getKey());
1482 if(obj && obj->m_known_by_count > 0)
1483 obj->m_known_by_count--;
1486 // Handle added objects
1487 writeU16((u8*)buf, added_objects.size());
1488 data_buffer.append(buf, 2);
1489 for(core::map<u16, bool>::Iterator
1490 i = added_objects.getIterator();
1491 i.atEnd()==false; i++)
1494 u16 id = i.getNode()->getKey();
1495 ServerActiveObject* obj = m_env.getActiveObject(id);
1498 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1500 dstream<<"WARNING: "<<__FUNCTION_NAME
1501 <<": NULL object"<<std::endl;
1503 type = obj->getType();
1505 // Add to data buffer for sending
1506 writeU16((u8*)buf, id);
1507 data_buffer.append(buf, 2);
1508 writeU8((u8*)buf, type);
1509 data_buffer.append(buf, 1);
1512 data_buffer.append(serializeLongString(
1513 obj->getClientInitializationData()));
1515 data_buffer.append(serializeLongString(""));
1517 // Add to known objects
1518 client->m_known_objects.insert(i.getNode()->getKey(), false);
1521 obj->m_known_by_count++;
1525 SharedBuffer<u8> reply(2 + data_buffer.size());
1526 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1527 memcpy((char*)&reply[2], data_buffer.c_str(),
1528 data_buffer.size());
1530 m_con.Send(client->peer_id, 0, reply, true);
1532 dstream<<"INFO: Server: Sent object remove/add: "
1533 <<removed_objects.size()<<" removed, "
1534 <<added_objects.size()<<" added, "
1535 <<"packet size is "<<reply.getSize()<<std::endl;
1540 Collect a list of all the objects known by the clients
1541 and report it back to the environment.
1544 core::map<u16, bool> all_known_objects;
1546 for(core::map<u16, RemoteClient*>::Iterator
1547 i = m_clients.getIterator();
1548 i.atEnd() == false; i++)
1550 RemoteClient *client = i.getNode()->getValue();
1551 // Go through all known objects of client
1552 for(core::map<u16, bool>::Iterator
1553 i = client->m_known_objects.getIterator();
1554 i.atEnd()==false; i++)
1556 u16 id = i.getNode()->getKey();
1557 all_known_objects[id] = true;
1561 m_env.setKnownActiveObjects(whatever);
1567 Send object messages
1570 JMutexAutoLock envlock(m_env_mutex);
1571 JMutexAutoLock conlock(m_con_mutex);
1573 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1576 // Value = data sent by object
1577 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1579 // Get active object messages from environment
1582 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1586 core::list<ActiveObjectMessage>* message_list = NULL;
1587 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1588 n = buffered_messages.find(aom.id);
1591 message_list = new core::list<ActiveObjectMessage>;
1592 buffered_messages.insert(aom.id, message_list);
1596 message_list = n->getValue();
1598 message_list->push_back(aom);
1601 // Route data to every client
1602 for(core::map<u16, RemoteClient*>::Iterator
1603 i = m_clients.getIterator();
1604 i.atEnd()==false; i++)
1606 RemoteClient *client = i.getNode()->getValue();
1607 std::string reliable_data;
1608 std::string unreliable_data;
1609 // Go through all objects in message buffer
1610 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1611 j = buffered_messages.getIterator();
1612 j.atEnd()==false; j++)
1614 // If object is not known by client, skip it
1615 u16 id = j.getNode()->getKey();
1616 if(client->m_known_objects.find(id) == NULL)
1618 // Get message list of object
1619 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1620 // Go through every message
1621 for(core::list<ActiveObjectMessage>::Iterator
1622 k = list->begin(); k != list->end(); k++)
1624 // Compose the full new data with header
1625 ActiveObjectMessage aom = *k;
1626 std::string new_data;
1629 writeU16((u8*)&buf[0], aom.id);
1630 new_data.append(buf, 2);
1632 new_data += serializeString(aom.datastring);
1633 // Add data to buffer
1635 reliable_data += new_data;
1637 unreliable_data += new_data;
1641 reliable_data and unreliable_data are now ready.
1644 if(reliable_data.size() > 0)
1646 SharedBuffer<u8> reply(2 + reliable_data.size());
1647 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1648 memcpy((char*)&reply[2], reliable_data.c_str(),
1649 reliable_data.size());
1651 m_con.Send(client->peer_id, 0, reply, true);
1653 if(unreliable_data.size() > 0)
1655 SharedBuffer<u8> reply(2 + unreliable_data.size());
1656 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1657 memcpy((char*)&reply[2], unreliable_data.c_str(),
1658 unreliable_data.size());
1659 // Send as unreliable
1660 m_con.Send(client->peer_id, 0, reply, false);
1663 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1665 dstream<<"INFO: Server: Size of object message data: "
1666 <<"reliable: "<<reliable_data.size()
1667 <<", unreliable: "<<unreliable_data.size()
1672 // Clear buffered_messages
1673 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1674 i = buffered_messages.getIterator();
1675 i.atEnd()==false; i++)
1677 delete i.getNode()->getValue();
1681 } // enable_experimental
1684 Send queued-for-sending map edit events.
1687 // Don't send too many at a time
1690 // Single change sending is disabled if queue size is not small
1691 bool disable_single_change_sending = false;
1692 if(m_unsent_map_edit_queue.size() >= 4)
1693 disable_single_change_sending = true;
1695 bool got_any_events = false;
1697 // We'll log the amount of each
1700 while(m_unsent_map_edit_queue.size() != 0)
1702 got_any_events = true;
1704 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1706 // Players far away from the change are stored here.
1707 // Instead of sending the changes, MapBlocks are set not sent
1709 core::list<u16> far_players;
1711 if(event->type == MEET_ADDNODE)
1713 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1714 prof.add("MEET_ADDNODE", 1);
1715 if(disable_single_change_sending)
1716 sendAddNode(event->p, event->n, event->already_known_by_peer,
1719 sendAddNode(event->p, event->n, event->already_known_by_peer,
1722 else if(event->type == MEET_REMOVENODE)
1724 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1725 prof.add("MEET_REMOVENODE", 1);
1726 if(disable_single_change_sending)
1727 sendRemoveNode(event->p, event->already_known_by_peer,
1730 sendRemoveNode(event->p, event->already_known_by_peer,
1733 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1735 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1736 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1737 setBlockNotSent(event->p);
1739 else if(event->type == MEET_OTHER)
1741 dstream<<"Server: MEET_OTHER"<<std::endl;
1742 prof.add("MEET_OTHER", 1);
1743 for(core::map<v3s16, bool>::Iterator
1744 i = event->modified_blocks.getIterator();
1745 i.atEnd()==false; i++)
1747 v3s16 p = i.getNode()->getKey();
1753 prof.add("unknown", 1);
1754 dstream<<"WARNING: Server: Unknown MapEditEvent "
1755 <<((u32)event->type)<<std::endl;
1759 Set blocks not sent to far players
1761 if(far_players.size() > 0)
1763 // Convert list format to that wanted by SetBlocksNotSent
1764 core::map<v3s16, MapBlock*> modified_blocks2;
1765 for(core::map<v3s16, bool>::Iterator
1766 i = event->modified_blocks.getIterator();
1767 i.atEnd()==false; i++)
1769 v3s16 p = i.getNode()->getKey();
1770 modified_blocks2.insert(p,
1771 m_env.getMap().getBlockNoCreateNoEx(p));
1773 // Set blocks not sent
1774 for(core::list<u16>::Iterator
1775 i = far_players.begin();
1776 i != far_players.end(); i++)
1779 RemoteClient *client = getClient(peer_id);
1782 client->SetBlocksNotSent(modified_blocks2);
1788 /*// Don't send too many at a time
1790 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1796 dstream<<"Server: MapEditEvents:"<<std::endl;
1797 prof.print(dstream);
1803 Send object positions
1804 TODO: Get rid of MapBlockObjects
1807 float &counter = m_objectdata_timer;
1809 if(counter >= g_settings->getFloat("objectdata_interval"))
1811 JMutexAutoLock lock1(m_env_mutex);
1812 JMutexAutoLock lock2(m_con_mutex);
1814 ScopeProfiler sp(g_profiler, "Server: sending mbo positions");
1816 SendObjectData(counter);
1823 Trigger emergethread (it somehow gets to a non-triggered but
1824 bysy state sometimes)
1827 float &counter = m_emergethread_trigger_timer;
1833 m_emergethread.trigger();
1837 // Save map, players and auth stuff
1839 float &counter = m_savemap_timer;
1841 if(counter >= g_settings->getFloat("server_map_save_interval"))
1845 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1848 if(m_authmanager.isModified())
1849 m_authmanager.save();
1852 if(m_banmanager.isModified())
1853 m_banmanager.save();
1856 JMutexAutoLock lock(m_env_mutex);
1858 /*// Unload unused data (delete from memory)
1859 m_env.getMap().unloadUnusedData(
1860 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1862 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1863 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1866 // Save only changed parts
1867 m_env.getMap().save(true);
1869 /*if(deleted_count > 0)
1871 dout_server<<"Server: Unloaded "<<deleted_count
1872 <<" blocks from memory"<<std::endl;
1876 m_env.serializePlayers(m_mapsavedir);
1878 // Save environment metadata
1879 m_env.saveMeta(m_mapsavedir);
1884 void Server::Receive()
1886 DSTACK(__FUNCTION_NAME);
1887 u32 data_maxsize = 10000;
1888 Buffer<u8> data(data_maxsize);
1893 JMutexAutoLock conlock(m_con_mutex);
1894 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1897 // This has to be called so that the client list gets synced
1898 // with the peer list of the connection
1899 handlePeerChanges();
1901 ProcessData(*data, datasize, peer_id);
1903 catch(con::InvalidIncomingDataException &e)
1905 derr_server<<"Server::Receive(): "
1906 "InvalidIncomingDataException: what()="
1907 <<e.what()<<std::endl;
1909 catch(con::PeerNotFoundException &e)
1911 //NOTE: This is not needed anymore
1913 // The peer has been disconnected.
1914 // Find the associated player and remove it.
1916 /*JMutexAutoLock envlock(m_env_mutex);
1918 dout_server<<"ServerThread: peer_id="<<peer_id
1919 <<" has apparently closed connection. "
1920 <<"Removing player."<<std::endl;
1922 m_env.removePlayer(peer_id);*/
1926 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1928 DSTACK(__FUNCTION_NAME);
1929 // Environment is locked first.
1930 JMutexAutoLock envlock(m_env_mutex);
1931 JMutexAutoLock conlock(m_con_mutex);
1935 peer = m_con.GetPeer(peer_id);
1937 catch(con::PeerNotFoundException &e)
1939 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1940 <<peer_id<<" not found"<<std::endl;
1944 // drop player if is ip is banned
1945 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1946 SendAccessDenied(m_con, peer_id,
1947 L"Your ip is banned. Banned name was "
1948 +narrow_to_wide(m_banmanager.getBanName(
1949 peer->address.serializeString())));
1950 m_con.deletePeer(peer_id, false);
1954 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1962 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1964 if(command == TOSERVER_INIT)
1966 // [0] u16 TOSERVER_INIT
1967 // [2] u8 SER_FMT_VER_HIGHEST
1968 // [3] u8[20] player_name
1969 // [23] u8[28] password <--- can be sent without this, from old versions
1971 if(datasize < 2+1+PLAYERNAME_SIZE)
1974 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1975 <<peer->id<<std::endl;
1977 // First byte after command is maximum supported
1978 // serialization version
1979 u8 client_max = data[2];
1980 u8 our_max = SER_FMT_VER_HIGHEST;
1981 // Use the highest version supported by both
1982 u8 deployed = core::min_(client_max, our_max);
1983 // If it's lower than the lowest supported, give up.
1984 if(deployed < SER_FMT_VER_LOWEST)
1985 deployed = SER_FMT_VER_INVALID;
1987 //peer->serialization_version = deployed;
1988 getClient(peer->id)->pending_serialization_version = deployed;
1990 if(deployed == SER_FMT_VER_INVALID)
1992 derr_server<<DTIME<<"Server: Cannot negotiate "
1993 "serialization version with peer "
1994 <<peer_id<<std::endl;
1995 SendAccessDenied(m_con, peer_id,
1996 L"Your client is too old (map format)");
2001 Read and check network protocol version
2004 u16 net_proto_version = 0;
2005 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2007 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2010 getClient(peer->id)->net_proto_version = net_proto_version;
2012 if(net_proto_version == 0)
2014 SendAccessDenied(m_con, peer_id,
2015 L"Your client is too old. Please upgrade.");
2019 /* Uhh... this should actually be a warning but let's do it like this */
2020 if(net_proto_version < 2)
2022 SendAccessDenied(m_con, peer_id,
2023 L"Your client is too old. Please upgrade.");
2032 char playername[PLAYERNAME_SIZE];
2033 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2035 playername[i] = data[3+i];
2037 playername[PLAYERNAME_SIZE-1] = 0;
2039 if(playername[0]=='\0')
2041 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2042 SendAccessDenied(m_con, peer_id,
2047 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2049 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2050 SendAccessDenied(m_con, peer_id,
2051 L"Name contains unallowed characters");
2056 char password[PASSWORD_SIZE];
2057 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2059 // old version - assume blank password
2064 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2066 password[i] = data[23+i];
2068 password[PASSWORD_SIZE-1] = 0;
2071 std::string checkpwd;
2072 if(m_authmanager.exists(playername))
2074 checkpwd = m_authmanager.getPassword(playername);
2078 checkpwd = g_settings->get("default_password");
2081 /*dstream<<"Server: Client gave password '"<<password
2082 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2084 if(password != checkpwd && m_authmanager.exists(playername))
2086 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2087 <<": supplied invalid password for "
2088 <<playername<<std::endl;
2089 SendAccessDenied(m_con, peer_id, L"Invalid password");
2093 // Add player to auth manager
2094 if(m_authmanager.exists(playername) == false)
2096 derr_server<<DTIME<<"Server: adding player "<<playername
2097 <<" to auth manager"<<std::endl;
2098 m_authmanager.add(playername);
2099 m_authmanager.setPassword(playername, checkpwd);
2100 m_authmanager.setPrivs(playername,
2101 stringToPrivs(g_settings->get("default_privs")));
2102 m_authmanager.save();
2105 // Enforce user limit.
2106 // Don't enforce for users that have some admin right
2107 if(m_clients.size() >= g_settings->getU16("max_users") &&
2108 (m_authmanager.getPrivs(playername)
2109 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2110 playername != g_settings->get("name"))
2112 SendAccessDenied(m_con, peer_id, L"Too many users.");
2117 Player *player = emergePlayer(playername, password, peer_id);
2120 // DEBUG: Test serialization
2121 std::ostringstream test_os;
2122 player->serialize(test_os);
2123 dstream<<"Player serialization test: \""<<test_os.str()
2125 std::istringstream test_is(test_os.str());
2126 player->deSerialize(test_is);
2129 // If failed, cancel
2132 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2133 <<": failed to emerge player"<<std::endl;
2138 // If a client is already connected to the player, cancel
2139 if(player->peer_id != 0)
2141 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2142 <<" tried to connect to "
2143 "an already connected player (peer_id="
2144 <<player->peer_id<<")"<<std::endl;
2147 // Set client of player
2148 player->peer_id = peer_id;
2151 // Check if player doesn't exist
2153 throw con::InvalidIncomingDataException
2154 ("Server::ProcessData(): INIT: Player doesn't exist");
2156 /*// update name if it was supplied
2157 if(datasize >= 20+3)
2160 player->updateName((const char*)&data[3]);
2164 Answer with a TOCLIENT_INIT
2167 SharedBuffer<u8> reply(2+1+6+8);
2168 writeU16(&reply[0], TOCLIENT_INIT);
2169 writeU8(&reply[2], deployed);
2170 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2171 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2174 m_con.Send(peer_id, 0, reply, true);
2178 Send complete position information
2180 SendMovePlayer(player);
2185 if(command == TOSERVER_INIT2)
2187 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2188 <<peer->id<<std::endl;
2191 getClient(peer->id)->serialization_version
2192 = getClient(peer->id)->pending_serialization_version;
2195 Send some initialization data
2198 // Send player info to all players
2201 // Send inventory to player
2202 UpdateCrafting(peer->id);
2203 SendInventory(peer->id);
2205 // Send player items to all players
2210 Player *player = m_env.getPlayer(peer_id);
2211 SendPlayerHP(player);
2216 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2217 m_env.getTimeOfDay());
2218 m_con.Send(peer->id, 0, data, true);
2221 // Send information about server to player in chat
2222 SendChatMessage(peer_id, getStatusString());
2224 // Send information about joining in chat
2226 std::wstring name = L"unknown";
2227 Player *player = m_env.getPlayer(peer_id);
2229 name = narrow_to_wide(player->getName());
2231 std::wstring message;
2234 message += L" joined game";
2235 BroadcastChatMessage(message);
2238 // Warnings about protocol version can be issued here
2239 /*if(getClient(peer->id)->net_proto_version == 0)
2241 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2247 if(peer_ser_ver == SER_FMT_VER_INVALID)
2249 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2250 " serialization format invalid or not initialized."
2251 " Skipping incoming command="<<command<<std::endl;
2255 Player *player = m_env.getPlayer(peer_id);
2258 derr_server<<"Server::ProcessData(): Cancelling: "
2259 "No player for peer_id="<<peer_id
2263 if(command == TOSERVER_PLAYERPOS)
2265 if(datasize < 2+12+12+4+4)
2269 v3s32 ps = readV3S32(&data[start+2]);
2270 v3s32 ss = readV3S32(&data[start+2+12]);
2271 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2272 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2273 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2274 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2275 pitch = wrapDegrees(pitch);
2276 yaw = wrapDegrees(yaw);
2277 player->setPosition(position);
2278 player->setSpeed(speed);
2279 player->setPitch(pitch);
2280 player->setYaw(yaw);
2282 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2283 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2284 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2286 else if(command == TOSERVER_GOTBLOCKS)
2299 u16 count = data[2];
2300 for(u16 i=0; i<count; i++)
2302 if((s16)datasize < 2+1+(i+1)*6)
2303 throw con::InvalidIncomingDataException
2304 ("GOTBLOCKS length is too short");
2305 v3s16 p = readV3S16(&data[2+1+i*6]);
2306 /*dstream<<"Server: GOTBLOCKS ("
2307 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2308 RemoteClient *client = getClient(peer_id);
2309 client->GotBlock(p);
2312 else if(command == TOSERVER_DELETEDBLOCKS)
2325 u16 count = data[2];
2326 for(u16 i=0; i<count; i++)
2328 if((s16)datasize < 2+1+(i+1)*6)
2329 throw con::InvalidIncomingDataException
2330 ("DELETEDBLOCKS length is too short");
2331 v3s16 p = readV3S16(&data[2+1+i*6]);
2332 /*dstream<<"Server: DELETEDBLOCKS ("
2333 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2334 RemoteClient *client = getClient(peer_id);
2335 client->SetBlockNotSent(p);
2338 else if(command == TOSERVER_CLICK_OBJECT)
2343 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2348 [2] u8 button (0=left, 1=right)
2353 u8 button = readU8(&data[2]);
2355 p.X = readS16(&data[3]);
2356 p.Y = readS16(&data[5]);
2357 p.Z = readS16(&data[7]);
2358 s16 id = readS16(&data[9]);
2359 //u16 item_i = readU16(&data[11]);
2361 MapBlock *block = NULL;
2364 block = m_env.getMap().getBlockNoCreate(p);
2366 catch(InvalidPositionException &e)
2368 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2372 MapBlockObject *obj = block->getObject(id);
2376 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2380 //TODO: Check that object is reasonably close
2385 InventoryList *ilist = player->inventory.getList("main");
2386 if(g_settings->getBool("creative_mode") == false && ilist != NULL)
2389 // Skip if inventory has no free space
2390 if(ilist->getUsedSlots() == ilist->getSize())
2392 dout_server<<"Player inventory has no free space"<<std::endl;
2397 Create the inventory item
2399 InventoryItem *item = NULL;
2400 // If it is an item-object, take the item from it
2401 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2403 item = ((ItemObject*)obj)->createInventoryItem();
2405 // Else create an item of the object
2408 item = new MapBlockObjectItem
2409 (obj->getInventoryString());
2412 // Add to inventory and send inventory
2413 ilist->addItem(item);
2414 UpdateCrafting(player->peer_id);
2415 SendInventory(player->peer_id);
2418 // Remove from block
2419 block->removeObject(id);
2422 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2427 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2433 [2] u8 button (0=left, 1=right)
2437 u8 button = readU8(&data[2]);
2438 u16 id = readS16(&data[3]);
2439 u16 item_i = readU16(&data[11]);
2441 ServerActiveObject *obj = m_env.getActiveObject(id);
2445 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2450 // Skip if object has been removed
2454 //TODO: Check that object is reasonably close
2456 // Left click, pick object up (usually)
2460 Try creating inventory item
2462 InventoryItem *item = obj->createPickedUpItem();
2466 InventoryList *ilist = player->inventory.getList("main");
2469 if(g_settings->getBool("creative_mode") == false)
2471 // Skip if inventory has no free space
2472 if(ilist->roomForItem(item) == false)
2474 dout_server<<"Player inventory has no free space"<<std::endl;
2478 // Add to inventory and send inventory
2479 ilist->addItem(item);
2480 UpdateCrafting(player->peer_id);
2481 SendInventory(player->peer_id);
2484 // Remove object from environment
2485 obj->m_removed = true;
2491 Item cannot be picked up. Punch it instead.
2494 ToolItem *titem = NULL;
2495 std::string toolname = "";
2497 InventoryList *mlist = player->inventory.getList("main");
2500 InventoryItem *item = mlist->getItem(item_i);
2501 if(item && (std::string)item->getName() == "ToolItem")
2503 titem = (ToolItem*)item;
2504 toolname = titem->getToolName();
2508 v3f playerpos = player->getPosition();
2509 v3f objpos = obj->getBasePosition();
2510 v3f dir = (objpos - playerpos).normalize();
2512 u16 wear = obj->punch(toolname, dir);
2516 bool weared_out = titem->addWear(wear);
2518 mlist->deleteItem(item_i);
2519 SendInventory(player->peer_id);
2523 // Right click, do something with object
2526 // Track hp changes super-crappily
2527 u16 oldhp = player->hp;
2530 obj->rightClick(player);
2533 if(player->hp != oldhp)
2535 SendPlayerHP(player);
2539 else if(command == TOSERVER_GROUND_ACTION)
2547 [3] v3s16 nodepos_undersurface
2548 [9] v3s16 nodepos_abovesurface
2553 2: stop digging (all parameters ignored)
2554 3: digging completed
2556 u8 action = readU8(&data[2]);
2558 p_under.X = readS16(&data[3]);
2559 p_under.Y = readS16(&data[5]);
2560 p_under.Z = readS16(&data[7]);
2562 p_over.X = readS16(&data[9]);
2563 p_over.Y = readS16(&data[11]);
2564 p_over.Z = readS16(&data[13]);
2565 u16 item_i = readU16(&data[15]);
2567 //TODO: Check that target is reasonably close
2575 NOTE: This can be used in the future to check if
2576 somebody is cheating, by checking the timing.
2583 else if(action == 2)
2586 RemoteClient *client = getClient(peer->id);
2587 JMutexAutoLock digmutex(client->m_dig_mutex);
2588 client->m_dig_tool_item = -1;
2593 3: Digging completed
2595 else if(action == 3)
2597 // Mandatory parameter; actually used for nothing
2598 core::map<v3s16, MapBlock*> modified_blocks;
2600 content_t material = CONTENT_IGNORE;
2601 u8 mineral = MINERAL_NONE;
2603 bool cannot_remove_node = false;
2607 MapNode n = m_env.getMap().getNode(p_under);
2609 mineral = n.getMineral();
2610 // Get material at position
2611 material = n.getContent();
2612 // If not yet cancelled
2613 if(cannot_remove_node == false)
2615 // If it's not diggable, do nothing
2616 if(content_diggable(material) == false)
2618 derr_server<<"Server: Not finishing digging: "
2619 <<"Node not diggable"
2621 cannot_remove_node = true;
2624 // If not yet cancelled
2625 if(cannot_remove_node == false)
2627 // Get node metadata
2628 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2629 if(meta && meta->nodeRemovalDisabled() == true)
2631 derr_server<<"Server: Not finishing digging: "
2632 <<"Node metadata disables removal"
2634 cannot_remove_node = true;
2638 catch(InvalidPositionException &e)
2640 derr_server<<"Server: Not finishing digging: Node not found."
2641 <<" Adding block to emerge queue."
2643 m_emerge_queue.addBlock(peer_id,
2644 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2645 cannot_remove_node = true;
2648 // Make sure the player is allowed to do it
2649 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2651 dstream<<"Player "<<player->getName()<<" cannot remove node"
2652 <<" because privileges are "<<getPlayerPrivs(player)
2654 cannot_remove_node = true;
2658 If node can't be removed, set block to be re-sent to
2661 if(cannot_remove_node)
2663 derr_server<<"Server: Not finishing digging."<<std::endl;
2665 // Client probably has wrong data.
2666 // Set block not sent, so that client will get
2668 dstream<<"Client "<<peer_id<<" tried to dig "
2669 <<"node; but node cannot be removed."
2670 <<" setting MapBlock not sent."<<std::endl;
2671 RemoteClient *client = getClient(peer_id);
2672 v3s16 blockpos = getNodeBlockPos(p_under);
2673 client->SetBlockNotSent(blockpos);
2679 Send the removal to all close-by players.
2680 - If other player is close, send REMOVENODE
2681 - Otherwise set blocks not sent
2683 core::list<u16> far_players;
2684 sendRemoveNode(p_under, peer_id, &far_players, 30);
2687 Update and send inventory
2690 if(g_settings->getBool("creative_mode") == false)
2695 InventoryList *mlist = player->inventory.getList("main");
2698 InventoryItem *item = mlist->getItem(item_i);
2699 if(item && (std::string)item->getName() == "ToolItem")
2701 ToolItem *titem = (ToolItem*)item;
2702 std::string toolname = titem->getToolName();
2704 // Get digging properties for material and tool
2705 DiggingProperties prop =
2706 getDiggingProperties(material, toolname);
2708 if(prop.diggable == false)
2710 derr_server<<"Server: WARNING: Player digged"
2711 <<" with impossible material + tool"
2712 <<" combination"<<std::endl;
2715 bool weared_out = titem->addWear(prop.wear);
2719 mlist->deleteItem(item_i);
2725 Add dug item to inventory
2728 InventoryItem *item = NULL;
2730 if(mineral != MINERAL_NONE)
2731 item = getDiggedMineralItem(mineral);
2736 std::string &dug_s = content_features(material).dug_item;
2739 std::istringstream is(dug_s, std::ios::binary);
2740 item = InventoryItem::deSerialize(is);
2746 // Add a item to inventory
2747 player->inventory.addItem("main", item);
2750 UpdateCrafting(player->peer_id);
2751 SendInventory(player->peer_id);
2756 if(mineral != MINERAL_NONE)
2757 item = getDiggedMineralItem(mineral);
2762 std::string &extra_dug_s = content_features(material).extra_dug_item;
2763 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2764 if(extra_dug_s != "" && extra_rarity != 0
2765 && myrand() % extra_rarity == 0)
2767 std::istringstream is(extra_dug_s, std::ios::binary);
2768 item = InventoryItem::deSerialize(is);
2774 // Add a item to inventory
2775 player->inventory.addItem("main", item);
2778 UpdateCrafting(player->peer_id);
2779 SendInventory(player->peer_id);
2785 (this takes some time so it is done after the quick stuff)
2788 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2790 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2793 Set blocks not sent to far players
2795 for(core::list<u16>::Iterator
2796 i = far_players.begin();
2797 i != far_players.end(); i++)
2800 RemoteClient *client = getClient(peer_id);
2803 client->SetBlocksNotSent(modified_blocks);
2810 else if(action == 1)
2813 InventoryList *ilist = player->inventory.getList("main");
2818 InventoryItem *item = ilist->getItem(item_i);
2820 // If there is no item, it is not possible to add it anywhere
2825 Handle material items
2827 if(std::string("MaterialItem") == item->getName())
2830 // Don't add a node if this is not a free space
2831 MapNode n2 = m_env.getMap().getNode(p_over);
2832 bool no_enough_privs =
2833 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2835 dstream<<"Player "<<player->getName()<<" cannot add node"
2836 <<" because privileges are "<<getPlayerPrivs(player)
2839 if(content_features(n2).buildable_to == false
2842 // Client probably has wrong data.
2843 // Set block not sent, so that client will get
2845 dstream<<"Client "<<peer_id<<" tried to place"
2846 <<" node in invalid position; setting"
2847 <<" MapBlock not sent."<<std::endl;
2848 RemoteClient *client = getClient(peer_id);
2849 v3s16 blockpos = getNodeBlockPos(p_over);
2850 client->SetBlockNotSent(blockpos);
2854 catch(InvalidPositionException &e)
2856 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2857 <<" Adding block to emerge queue."
2859 m_emerge_queue.addBlock(peer_id,
2860 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2864 // Reset build time counter
2865 getClient(peer->id)->m_time_from_building = 0.0;
2868 MaterialItem *mitem = (MaterialItem*)item;
2870 n.setContent(mitem->getMaterial());
2872 // Calculate direction for wall mounted stuff
2873 if(content_features(n).wall_mounted)
2874 n.param2 = packDir(p_under - p_over);
2876 // Calculate the direction for furnaces and chests and stuff
2877 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2879 v3f playerpos = player->getPosition();
2880 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2881 blockpos = blockpos.normalize();
2883 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2897 Send to all close-by players
2899 core::list<u16> far_players;
2900 sendAddNode(p_over, n, 0, &far_players, 30);
2905 InventoryList *ilist = player->inventory.getList("main");
2906 if(g_settings->getBool("creative_mode") == false && ilist)
2908 // Remove from inventory and send inventory
2909 if(mitem->getCount() == 1)
2910 ilist->deleteItem(item_i);
2914 UpdateCrafting(peer_id);
2915 SendInventory(peer_id);
2921 This takes some time so it is done after the quick stuff
2923 core::map<v3s16, MapBlock*> modified_blocks;
2925 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2927 std::string p_name = std::string(player->getName());
2928 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2931 Set blocks not sent to far players
2933 for(core::list<u16>::Iterator
2934 i = far_players.begin();
2935 i != far_players.end(); i++)
2938 RemoteClient *client = getClient(peer_id);
2941 client->SetBlocksNotSent(modified_blocks);
2945 Calculate special events
2948 /*if(n.d == CONTENT_MESE)
2951 for(s16 z=-1; z<=1; z++)
2952 for(s16 y=-1; y<=1; y++)
2953 for(s16 x=-1; x<=1; x++)
2960 Place other item (not a block)
2964 v3s16 blockpos = getNodeBlockPos(p_over);
2967 Check that the block is loaded so that the item
2968 can properly be added to the static list too
2970 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2973 derr_server<<"Error while placing object: "
2974 "block not found"<<std::endl;
2979 If in creative mode, item dropping is disabled unless
2980 player has build privileges
2982 if(g_settings->getBool("creative_mode") &&
2983 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2985 derr_server<<"Not allowing player to drop item: "
2986 "creative mode and no build privs"<<std::endl;
2990 dout_server<<"Placing a miscellaneous item on map"
2993 // Calculate a position for it
2994 v3f pos = intToFloat(p_over, BS);
2996 pos.Y -= BS*0.25; // let it drop a bit
2998 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2999 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3004 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
3008 derr_server<<"WARNING: item resulted in NULL object, "
3009 <<"not placing onto map"
3014 // Add the object to the environment
3015 m_env.addActiveObject(obj);
3017 dout_server<<"Placed object"<<std::endl;
3019 if(g_settings->getBool("creative_mode") == false)
3021 // Delete the right amount of items from the slot
3022 u16 dropcount = item->getDropCount();
3024 // Delete item if all gone
3025 if(item->getCount() <= dropcount)
3027 if(item->getCount() < dropcount)
3028 dstream<<"WARNING: Server: dropped more items"
3029 <<" than the slot contains"<<std::endl;
3031 InventoryList *ilist = player->inventory.getList("main");
3033 // Remove from inventory and send inventory
3034 ilist->deleteItem(item_i);
3036 // Else decrement it
3038 item->remove(dropcount);
3041 UpdateCrafting(peer_id);
3042 SendInventory(peer_id);
3050 Catch invalid actions
3054 derr_server<<"WARNING: Server: Invalid action "
3055 <<action<<std::endl;
3059 else if(command == TOSERVER_RELEASE)
3068 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3071 else if(command == TOSERVER_SIGNTEXT)
3073 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3082 std::string datastring((char*)&data[2], datasize-2);
3083 std::istringstream is(datastring, std::ios_base::binary);
3086 is.read((char*)buf, 6);
3087 v3s16 blockpos = readV3S16(buf);
3088 is.read((char*)buf, 2);
3089 s16 id = readS16(buf);
3090 is.read((char*)buf, 2);
3091 u16 textlen = readU16(buf);
3093 for(u16 i=0; i<textlen; i++)
3095 is.read((char*)buf, 1);
3096 text += (char)buf[0];
3099 MapBlock *block = NULL;
3102 block = m_env.getMap().getBlockNoCreate(blockpos);
3104 catch(InvalidPositionException &e)
3106 derr_server<<"Error while setting sign text: "
3107 "block not found"<<std::endl;
3111 MapBlockObject *obj = block->getObject(id);
3114 derr_server<<"Error while setting sign text: "
3115 "object not found"<<std::endl;
3119 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3121 derr_server<<"Error while setting sign text: "
3122 "object is not a sign"<<std::endl;
3126 ((SignObject*)obj)->setText(text);
3128 obj->getBlock()->setChangedFlag();
3130 else if(command == TOSERVER_SIGNNODETEXT)
3132 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3140 std::string datastring((char*)&data[2], datasize-2);
3141 std::istringstream is(datastring, std::ios_base::binary);
3144 is.read((char*)buf, 6);
3145 v3s16 p = readV3S16(buf);
3146 is.read((char*)buf, 2);
3147 u16 textlen = readU16(buf);
3149 for(u16 i=0; i<textlen; i++)
3151 is.read((char*)buf, 1);
3152 text += (char)buf[0];
3155 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3158 if(meta->typeId() != CONTENT_SIGN_WALL)
3160 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3161 signmeta->setText(text);
3163 v3s16 blockpos = getNodeBlockPos(p);
3164 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3167 block->setChangedFlag();
3170 for(core::map<u16, RemoteClient*>::Iterator
3171 i = m_clients.getIterator();
3172 i.atEnd()==false; i++)
3174 RemoteClient *client = i.getNode()->getValue();
3175 client->SetBlockNotSent(blockpos);
3178 else if(command == TOSERVER_INVENTORY_ACTION)
3180 /*// Ignore inventory changes if in creative mode
3181 if(g_settings->getBool("creative_mode") == true)
3183 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3187 // Strip command and create a stream
3188 std::string datastring((char*)&data[2], datasize-2);
3189 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3190 std::istringstream is(datastring, std::ios_base::binary);
3192 InventoryAction *a = InventoryAction::deSerialize(is);
3197 c.current_player = player;
3200 Handle craftresult specially if not in creative mode
3202 bool disable_action = false;
3203 if(a->getType() == IACTION_MOVE
3204 && g_settings->getBool("creative_mode") == false)
3206 IMoveAction *ma = (IMoveAction*)a;
3207 if(ma->to_inv == "current_player" &&
3208 ma->from_inv == "current_player")
3210 InventoryList *rlist = player->inventory.getList("craftresult");
3212 InventoryList *clist = player->inventory.getList("craft");
3214 InventoryList *mlist = player->inventory.getList("main");
3217 Craftresult is no longer preview if something
3220 if(ma->to_list == "craftresult"
3221 && ma->from_list != "craftresult")
3223 // If it currently is a preview, remove
3225 if(player->craftresult_is_preview)
3227 rlist->deleteItem(0);
3229 player->craftresult_is_preview = false;
3232 Crafting takes place if this condition is true.
3234 if(player->craftresult_is_preview &&
3235 ma->from_list == "craftresult")
3237 player->craftresult_is_preview = false;
3238 clist->decrementMaterials(1);
3241 If the craftresult is placed on itself, move it to
3242 main inventory instead of doing the action
3244 if(ma->to_list == "craftresult"
3245 && ma->from_list == "craftresult")
3247 disable_action = true;
3249 InventoryItem *item1 = rlist->changeItem(0, NULL);
3250 mlist->addItem(item1);
3253 // Disallow moving items if not allowed to build
3254 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3258 // if it's a locking chest, only allow the owner or server admins to move items
3259 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3261 Strfnd fn(ma->from_inv);
3262 std::string id0 = fn.next(":");
3263 if(id0 == "nodemeta")
3266 p.X = stoi(fn.next(","));
3267 p.Y = stoi(fn.next(","));
3268 p.Z = stoi(fn.next(","));
3269 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3270 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3271 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3272 if (lcm->getOwner() != player->getName())
3277 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3279 Strfnd fn(ma->to_inv);
3280 std::string id0 = fn.next(":");
3281 if(id0 == "nodemeta")
3284 p.X = stoi(fn.next(","));
3285 p.Y = stoi(fn.next(","));
3286 p.Z = stoi(fn.next(","));
3287 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3288 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3289 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3290 if (lcm->getOwner() != player->getName())
3297 if(disable_action == false)
3299 // Feed action to player inventory
3307 UpdateCrafting(player->peer_id);
3308 SendInventory(player->peer_id);
3313 dstream<<"TOSERVER_INVENTORY_ACTION: "
3314 <<"InventoryAction::deSerialize() returned NULL"
3318 else if(command == TOSERVER_CHAT_MESSAGE)
3326 std::string datastring((char*)&data[2], datasize-2);
3327 std::istringstream is(datastring, std::ios_base::binary);
3330 is.read((char*)buf, 2);
3331 u16 len = readU16(buf);
3333 std::wstring message;
3334 for(u16 i=0; i<len; i++)
3336 is.read((char*)buf, 2);
3337 message += (wchar_t)readU16(buf);
3340 // Get player name of this client
3341 std::wstring name = narrow_to_wide(player->getName());
3343 // Line to send to players
3345 // Whether to send to the player that sent the line
3346 bool send_to_sender = false;
3347 // Whether to send to other players
3348 bool send_to_others = false;
3350 // Local player gets all privileges regardless of
3351 // what's set on their account.
3352 u64 privs = getPlayerPrivs(player);
3355 if(message[0] == L'/')
3357 size_t strip_size = 1;
3358 if (message[1] == L'#') // support old-style commans
3360 message = message.substr(strip_size);
3362 WStrfnd f1(message);
3363 f1.next(L" "); // Skip over /#whatever
3364 std::wstring paramstring = f1.next(L"");
3366 ServerCommandContext *ctx = new ServerCommandContext(
3367 str_split(message, L' '),
3374 std::wstring reply(processServerCommand(ctx));
3375 send_to_sender = ctx->flags & SEND_TO_SENDER;
3376 send_to_others = ctx->flags & SEND_TO_OTHERS;
3378 if (ctx->flags & SEND_NO_PREFIX)
3381 line += L"Server: " + reply;
3388 if(privs & PRIV_SHOUT)
3394 send_to_others = true;
3398 line += L"Server: You are not allowed to shout";
3399 send_to_sender = true;
3405 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3408 Send the message to clients
3410 for(core::map<u16, RemoteClient*>::Iterator
3411 i = m_clients.getIterator();
3412 i.atEnd() == false; i++)
3414 // Get client and check that it is valid
3415 RemoteClient *client = i.getNode()->getValue();
3416 assert(client->peer_id == i.getNode()->getKey());
3417 if(client->serialization_version == SER_FMT_VER_INVALID)
3421 bool sender_selected = (peer_id == client->peer_id);
3422 if(sender_selected == true && send_to_sender == false)
3424 if(sender_selected == false && send_to_others == false)
3427 SendChatMessage(client->peer_id, line);
3431 else if(command == TOSERVER_DAMAGE)
3433 if(g_settings->getBool("enable_damage"))
3435 std::string datastring((char*)&data[2], datasize-2);
3436 std::istringstream is(datastring, std::ios_base::binary);
3437 u8 damage = readU8(is);
3438 if(player->hp > damage)
3440 player->hp -= damage;
3446 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3449 v3f pos = findSpawnPos(m_env.getServerMap());
3450 player->setPosition(pos);
3452 SendMovePlayer(player);
3453 SendPlayerHP(player);
3455 //TODO: Throw items around
3459 SendPlayerHP(player);
3461 else if(command == TOSERVER_PASSWORD)
3464 [0] u16 TOSERVER_PASSWORD
3465 [2] u8[28] old password
3466 [30] u8[28] new password
3469 if(datasize != 2+PASSWORD_SIZE*2)
3471 /*char password[PASSWORD_SIZE];
3472 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3473 password[i] = data[2+i];
3474 password[PASSWORD_SIZE-1] = 0;*/
3476 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3484 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3486 char c = data[2+PASSWORD_SIZE+i];
3492 dstream<<"Server: Client requests a password change from "
3493 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3495 std::string playername = player->getName();
3497 if(m_authmanager.exists(playername) == false)
3499 dstream<<"Server: playername not found in authmanager"<<std::endl;
3500 // Wrong old password supplied!!
3501 SendChatMessage(peer_id, L"playername not found in authmanager");
3505 std::string checkpwd = m_authmanager.getPassword(playername);
3507 if(oldpwd != checkpwd)
3509 dstream<<"Server: invalid old password"<<std::endl;
3510 // Wrong old password supplied!!
3511 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3515 m_authmanager.setPassword(playername, newpwd);
3517 dstream<<"Server: password change successful for "<<playername
3519 SendChatMessage(peer_id, L"Password change successful");
3521 else if (command == TOSERVER_PLAYERITEM)
3526 u16 item = readU16(&data[2]);
3527 player->wieldItem(item);
3528 SendWieldedItem(player);
3532 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3533 "unknown command "<<command<<std::endl;
3537 catch(SendFailedException &e)
3539 derr_server<<"Server::ProcessData(): SendFailedException: "
3545 void Server::onMapEditEvent(MapEditEvent *event)
3547 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3548 if(m_ignore_map_edit_events)
3550 MapEditEvent *e = event->clone();
3551 m_unsent_map_edit_queue.push_back(e);
3554 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3556 if(id == "current_player")
3558 assert(c->current_player);
3559 return &(c->current_player->inventory);
3563 std::string id0 = fn.next(":");
3565 if(id0 == "nodemeta")
3568 p.X = stoi(fn.next(","));
3569 p.Y = stoi(fn.next(","));
3570 p.Z = stoi(fn.next(","));
3571 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3573 return meta->getInventory();
3574 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3575 <<"no metadata found"<<std::endl;
3579 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3582 void Server::inventoryModified(InventoryContext *c, std::string id)
3584 if(id == "current_player")
3586 assert(c->current_player);
3588 UpdateCrafting(c->current_player->peer_id);
3589 SendInventory(c->current_player->peer_id);
3594 std::string id0 = fn.next(":");
3596 if(id0 == "nodemeta")
3599 p.X = stoi(fn.next(","));
3600 p.Y = stoi(fn.next(","));
3601 p.Z = stoi(fn.next(","));
3602 v3s16 blockpos = getNodeBlockPos(p);
3604 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3606 meta->inventoryModified();
3608 for(core::map<u16, RemoteClient*>::Iterator
3609 i = m_clients.getIterator();
3610 i.atEnd()==false; i++)
3612 RemoteClient *client = i.getNode()->getValue();
3613 client->SetBlockNotSent(blockpos);
3619 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3622 core::list<PlayerInfo> Server::getPlayerInfo()
3624 DSTACK(__FUNCTION_NAME);
3625 JMutexAutoLock envlock(m_env_mutex);
3626 JMutexAutoLock conlock(m_con_mutex);
3628 core::list<PlayerInfo> list;
3630 core::list<Player*> players = m_env.getPlayers();
3632 core::list<Player*>::Iterator i;
3633 for(i = players.begin();
3634 i != players.end(); i++)
3638 Player *player = *i;
3641 con::Peer *peer = m_con.GetPeer(player->peer_id);
3642 // Copy info from peer to info struct
3644 info.address = peer->address;
3645 info.avg_rtt = peer->avg_rtt;
3647 catch(con::PeerNotFoundException &e)
3649 // Set dummy peer info
3651 info.address = Address(0,0,0,0,0);
3655 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3656 info.position = player->getPosition();
3658 list.push_back(info);
3665 void Server::peerAdded(con::Peer *peer)
3667 DSTACK(__FUNCTION_NAME);
3668 dout_server<<"Server::peerAdded(): peer->id="
3669 <<peer->id<<std::endl;
3672 c.type = PEER_ADDED;
3673 c.peer_id = peer->id;
3675 m_peer_change_queue.push_back(c);
3678 void Server::deletingPeer(con::Peer *peer, bool timeout)
3680 DSTACK(__FUNCTION_NAME);
3681 dout_server<<"Server::deletingPeer(): peer->id="
3682 <<peer->id<<", timeout="<<timeout<<std::endl;
3685 c.type = PEER_REMOVED;
3686 c.peer_id = peer->id;
3687 c.timeout = timeout;
3688 m_peer_change_queue.push_back(c);
3695 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3697 DSTACK(__FUNCTION_NAME);
3698 std::ostringstream os(std::ios_base::binary);
3700 writeU16(os, TOCLIENT_HP);
3704 std::string s = os.str();
3705 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3707 con.Send(peer_id, 0, data, true);
3710 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3711 const std::wstring &reason)
3713 DSTACK(__FUNCTION_NAME);
3714 std::ostringstream os(std::ios_base::binary);
3716 writeU16(os, TOCLIENT_ACCESS_DENIED);
3717 os<<serializeWideString(reason);
3720 std::string s = os.str();
3721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3723 con.Send(peer_id, 0, data, true);
3727 Non-static send methods
3730 void Server::SendObjectData(float dtime)
3732 DSTACK(__FUNCTION_NAME);
3734 core::map<v3s16, bool> stepped_blocks;
3736 for(core::map<u16, RemoteClient*>::Iterator
3737 i = m_clients.getIterator();
3738 i.atEnd() == false; i++)
3740 u16 peer_id = i.getNode()->getKey();
3741 RemoteClient *client = i.getNode()->getValue();
3742 assert(client->peer_id == peer_id);
3744 if(client->serialization_version == SER_FMT_VER_INVALID)
3747 client->SendObjectData(this, dtime, stepped_blocks);
3751 void Server::SendPlayerInfos()
3753 DSTACK(__FUNCTION_NAME);
3755 //JMutexAutoLock envlock(m_env_mutex);
3757 // Get connected players
3758 core::list<Player*> players = m_env.getPlayers(true);
3760 u32 player_count = players.getSize();
3761 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3763 SharedBuffer<u8> data(datasize);
3764 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3767 core::list<Player*>::Iterator i;
3768 for(i = players.begin();
3769 i != players.end(); i++)
3771 Player *player = *i;
3773 /*dstream<<"Server sending player info for player with "
3774 "peer_id="<<player->peer_id<<std::endl;*/
3776 writeU16(&data[start], player->peer_id);
3777 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3778 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3779 start += 2+PLAYERNAME_SIZE;
3782 //JMutexAutoLock conlock(m_con_mutex);
3785 m_con.SendToAll(0, data, true);
3788 void Server::SendInventory(u16 peer_id)
3790 DSTACK(__FUNCTION_NAME);
3792 Player* player = m_env.getPlayer(peer_id);
3799 std::ostringstream os;
3800 //os.imbue(std::locale("C"));
3802 player->inventory.serialize(os);
3804 std::string s = os.str();
3806 SharedBuffer<u8> data(s.size()+2);
3807 writeU16(&data[0], TOCLIENT_INVENTORY);
3808 memcpy(&data[2], s.c_str(), s.size());
3811 m_con.Send(peer_id, 0, data, true);
3814 std::string getWieldedItemString(const Player *player)
3816 const InventoryItem *item = player->getWieldItem();
3818 return std::string("");
3819 std::ostringstream os(std::ios_base::binary);
3820 item->serialize(os);
3824 void Server::SendWieldedItem(const Player* player)
3826 DSTACK(__FUNCTION_NAME);
3830 std::ostringstream os(std::ios_base::binary);
3832 writeU16(os, TOCLIENT_PLAYERITEM);
3834 writeU16(os, player->peer_id);
3835 os<<serializeString(getWieldedItemString(player));
3838 std::string s = os.str();
3839 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3841 m_con.SendToAll(0, data, true);
3844 void Server::SendPlayerItems()
3846 DSTACK(__FUNCTION_NAME);
3848 std::ostringstream os(std::ios_base::binary);
3849 core::list<Player *> players = m_env.getPlayers(true);
3851 writeU16(os, TOCLIENT_PLAYERITEM);
3852 writeU16(os, players.size());
3853 core::list<Player *>::Iterator i;
3854 for(i = players.begin(); i != players.end(); ++i)
3857 writeU16(os, p->peer_id);
3858 os<<serializeString(getWieldedItemString(p));
3862 std::string s = os.str();
3863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3865 m_con.SendToAll(0, data, true);
3868 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3870 DSTACK(__FUNCTION_NAME);
3872 std::ostringstream os(std::ios_base::binary);
3876 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3877 os.write((char*)buf, 2);
3880 writeU16(buf, message.size());
3881 os.write((char*)buf, 2);
3884 for(u32 i=0; i<message.size(); i++)
3888 os.write((char*)buf, 2);
3892 std::string s = os.str();
3893 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3895 m_con.Send(peer_id, 0, data, true);
3898 void Server::BroadcastChatMessage(const std::wstring &message)
3900 for(core::map<u16, RemoteClient*>::Iterator
3901 i = m_clients.getIterator();
3902 i.atEnd() == false; i++)
3904 // Get client and check that it is valid
3905 RemoteClient *client = i.getNode()->getValue();
3906 assert(client->peer_id == i.getNode()->getKey());
3907 if(client->serialization_version == SER_FMT_VER_INVALID)
3910 SendChatMessage(client->peer_id, message);
3914 void Server::SendPlayerHP(Player *player)
3916 SendHP(m_con, player->peer_id, player->hp);
3919 void Server::SendMovePlayer(Player *player)
3921 DSTACK(__FUNCTION_NAME);
3922 std::ostringstream os(std::ios_base::binary);
3924 writeU16(os, TOCLIENT_MOVE_PLAYER);
3925 writeV3F1000(os, player->getPosition());
3926 writeF1000(os, player->getPitch());
3927 writeF1000(os, player->getYaw());
3930 v3f pos = player->getPosition();
3931 f32 pitch = player->getPitch();
3932 f32 yaw = player->getYaw();
3933 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3934 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3941 std::string s = os.str();
3942 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3944 m_con.Send(player->peer_id, 0, data, true);
3947 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3948 core::list<u16> *far_players, float far_d_nodes)
3950 float maxd = far_d_nodes*BS;
3951 v3f p_f = intToFloat(p, BS);
3955 SharedBuffer<u8> reply(replysize);
3956 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3957 writeS16(&reply[2], p.X);
3958 writeS16(&reply[4], p.Y);
3959 writeS16(&reply[6], p.Z);
3961 for(core::map<u16, RemoteClient*>::Iterator
3962 i = m_clients.getIterator();
3963 i.atEnd() == false; i++)
3965 // Get client and check that it is valid
3966 RemoteClient *client = i.getNode()->getValue();
3967 assert(client->peer_id == i.getNode()->getKey());
3968 if(client->serialization_version == SER_FMT_VER_INVALID)
3971 // Don't send if it's the same one
3972 if(client->peer_id == ignore_id)
3978 Player *player = m_env.getPlayer(client->peer_id);
3981 // If player is far away, only set modified blocks not sent
3982 v3f player_pos = player->getPosition();
3983 if(player_pos.getDistanceFrom(p_f) > maxd)
3985 far_players->push_back(client->peer_id);
3992 m_con.Send(client->peer_id, 0, reply, true);
3996 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3997 core::list<u16> *far_players, float far_d_nodes)
3999 float maxd = far_d_nodes*BS;
4000 v3f p_f = intToFloat(p, BS);
4002 for(core::map<u16, RemoteClient*>::Iterator
4003 i = m_clients.getIterator();
4004 i.atEnd() == false; i++)
4006 // Get client and check that it is valid
4007 RemoteClient *client = i.getNode()->getValue();
4008 assert(client->peer_id == i.getNode()->getKey());
4009 if(client->serialization_version == SER_FMT_VER_INVALID)
4012 // Don't send if it's the same one
4013 if(client->peer_id == ignore_id)
4019 Player *player = m_env.getPlayer(client->peer_id);
4022 // If player is far away, only set modified blocks not sent
4023 v3f player_pos = player->getPosition();
4024 if(player_pos.getDistanceFrom(p_f) > maxd)
4026 far_players->push_back(client->peer_id);
4033 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4034 SharedBuffer<u8> reply(replysize);
4035 writeU16(&reply[0], TOCLIENT_ADDNODE);
4036 writeS16(&reply[2], p.X);
4037 writeS16(&reply[4], p.Y);
4038 writeS16(&reply[6], p.Z);
4039 n.serialize(&reply[8], client->serialization_version);
4042 m_con.Send(client->peer_id, 0, reply, true);
4046 void Server::setBlockNotSent(v3s16 p)
4048 for(core::map<u16, RemoteClient*>::Iterator
4049 i = m_clients.getIterator();
4050 i.atEnd()==false; i++)
4052 RemoteClient *client = i.getNode()->getValue();
4053 client->SetBlockNotSent(p);
4057 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4059 DSTACK(__FUNCTION_NAME);
4061 v3s16 p = block->getPos();
4065 bool completely_air = true;
4066 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4067 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4068 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4070 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4072 completely_air = false;
4073 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4078 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4080 dstream<<"[completely air] ";
4085 Create a packet with the block in the right format
4088 std::ostringstream os(std::ios_base::binary);
4089 block->serialize(os, ver);
4090 std::string s = os.str();
4091 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4093 u32 replysize = 8 + blockdata.getSize();
4094 SharedBuffer<u8> reply(replysize);
4095 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4096 writeS16(&reply[2], p.X);
4097 writeS16(&reply[4], p.Y);
4098 writeS16(&reply[6], p.Z);
4099 memcpy(&reply[8], *blockdata, blockdata.getSize());
4101 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4102 <<": \tpacket size: "<<replysize<<std::endl;*/
4107 m_con.Send(peer_id, 1, reply, true);
4110 void Server::SendBlocks(float dtime)
4112 DSTACK(__FUNCTION_NAME);
4114 JMutexAutoLock envlock(m_env_mutex);
4115 JMutexAutoLock conlock(m_con_mutex);
4117 //TimeTaker timer("Server::SendBlocks");
4119 core::array<PrioritySortedBlockTransfer> queue;
4121 s32 total_sending = 0;
4124 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4126 for(core::map<u16, RemoteClient*>::Iterator
4127 i = m_clients.getIterator();
4128 i.atEnd() == false; i++)
4130 RemoteClient *client = i.getNode()->getValue();
4131 assert(client->peer_id == i.getNode()->getKey());
4133 total_sending += client->SendingCount();
4135 if(client->serialization_version == SER_FMT_VER_INVALID)
4138 client->GetNextBlocks(this, dtime, queue);
4143 // Lowest priority number comes first.
4144 // Lowest is most important.
4147 for(u32 i=0; i<queue.size(); i++)
4149 //TODO: Calculate limit dynamically
4150 if(total_sending >= g_settings->getS32
4151 ("max_simultaneous_block_sends_server_total"))
4154 PrioritySortedBlockTransfer q = queue[i];
4156 MapBlock *block = NULL;
4159 block = m_env.getMap().getBlockNoCreate(q.pos);
4161 catch(InvalidPositionException &e)
4166 RemoteClient *client = getClient(q.peer_id);
4168 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4170 client->SentBlock(q.pos);
4180 void Server::UpdateCrafting(u16 peer_id)
4182 DSTACK(__FUNCTION_NAME);
4184 Player* player = m_env.getPlayer(peer_id);
4188 Calculate crafting stuff
4190 if(g_settings->getBool("creative_mode") == false)
4192 InventoryList *clist = player->inventory.getList("craft");
4193 InventoryList *rlist = player->inventory.getList("craftresult");
4195 if(rlist && rlist->getUsedSlots() == 0)
4196 player->craftresult_is_preview = true;
4198 if(rlist && player->craftresult_is_preview)
4200 rlist->clearItems();
4202 if(clist && rlist && player->craftresult_is_preview)
4204 InventoryItem *items[9];
4205 for(u16 i=0; i<9; i++)
4207 items[i] = clist->getItem(i);
4210 // Get result of crafting grid
4211 InventoryItem *result = craft_get_result(items);
4213 rlist->addItem(result);
4216 } // if creative_mode == false
4219 RemoteClient* Server::getClient(u16 peer_id)
4221 DSTACK(__FUNCTION_NAME);
4222 //JMutexAutoLock lock(m_con_mutex);
4223 core::map<u16, RemoteClient*>::Node *n;
4224 n = m_clients.find(peer_id);
4225 // A client should exist for all peers
4227 return n->getValue();
4230 std::wstring Server::getStatusString()
4232 std::wostringstream os(std::ios_base::binary);
4235 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4237 os<<L", uptime="<<m_uptime.get();
4238 // Information about clients
4240 for(core::map<u16, RemoteClient*>::Iterator
4241 i = m_clients.getIterator();
4242 i.atEnd() == false; i++)
4244 // Get client and check that it is valid
4245 RemoteClient *client = i.getNode()->getValue();
4246 assert(client->peer_id == i.getNode()->getKey());
4247 if(client->serialization_version == SER_FMT_VER_INVALID)
4250 Player *player = m_env.getPlayer(client->peer_id);
4251 // Get name of player
4252 std::wstring name = L"unknown";
4254 name = narrow_to_wide(player->getName());
4255 // Add name to information string
4259 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4260 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4261 if(g_settings->get("motd") != "")
4262 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4266 // Saves g_settings to configpath given at initialization
4267 void Server::saveConfig()
4269 if(m_configpath != "")
4270 g_settings->updateConfigFile(m_configpath.c_str());
4273 v3f findSpawnPos(ServerMap &map)
4275 //return v3f(50,50,50)*BS;
4278 s16 groundheight = 0;
4281 nodepos = v2s16(0,0);
4286 // Try to find a good place a few times
4287 for(s32 i=0; i<1000; i++)
4290 // We're going to try to throw the player to this position
4291 nodepos = v2s16(-range + (myrand()%(range*2)),
4292 -range + (myrand()%(range*2)));
4293 v2s16 sectorpos = getNodeSectorPos(nodepos);
4294 // Get sector (NOTE: Don't get because it's slow)
4295 //m_env.getMap().emergeSector(sectorpos);
4296 // Get ground height at point (fallbacks to heightmap function)
4297 groundheight = map.findGroundLevel(nodepos);
4298 // Don't go underwater
4299 if(groundheight < WATER_LEVEL)
4301 //dstream<<"-> Underwater"<<std::endl;
4304 // Don't go to high places
4305 if(groundheight > WATER_LEVEL + 4)
4307 //dstream<<"-> Underwater"<<std::endl;
4311 // Found a good place
4312 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4317 // If no suitable place was not found, go above water at least.
4318 if(groundheight < WATER_LEVEL)
4319 groundheight = WATER_LEVEL;
4321 return intToFloat(v3s16(
4328 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4331 Try to get an existing player
4333 Player *player = m_env.getPlayer(name);
4336 // If player is already connected, cancel
4337 if(player->peer_id != 0)
4339 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4344 player->peer_id = peer_id;
4346 // Reset inventory to creative if in creative mode
4347 if(g_settings->getBool("creative_mode"))
4349 // Warning: double code below
4350 // Backup actual inventory
4351 player->inventory_backup = new Inventory();
4352 *(player->inventory_backup) = player->inventory;
4353 // Set creative inventory
4354 craft_set_creative_inventory(player);
4361 If player with the wanted peer_id already exists, cancel.
4363 if(m_env.getPlayer(peer_id) != NULL)
4365 dstream<<"emergePlayer(): Player with wrong name but same"
4366 " peer_id already exists"<<std::endl;
4374 player = new ServerRemotePlayer();
4375 //player->peer_id = c.peer_id;
4376 //player->peer_id = PEER_ID_INEXISTENT;
4377 player->peer_id = peer_id;
4378 player->updateName(name);
4379 m_authmanager.add(name);
4380 m_authmanager.setPassword(name, password);
4381 m_authmanager.setPrivs(name,
4382 stringToPrivs(g_settings->get("default_privs")));
4388 dstream<<"Server: Finding spawn place for player \""
4389 <<player->getName()<<"\""<<std::endl;
4391 v3f pos = findSpawnPos(m_env.getServerMap());
4393 player->setPosition(pos);
4396 Add player to environment
4399 m_env.addPlayer(player);
4402 Add stuff to inventory
4405 if(g_settings->getBool("creative_mode"))
4407 // Warning: double code above
4408 // Backup actual inventory
4409 player->inventory_backup = new Inventory();
4410 *(player->inventory_backup) = player->inventory;
4411 // Set creative inventory
4412 craft_set_creative_inventory(player);
4414 else if(g_settings->getBool("give_initial_stuff"))
4416 craft_give_initial_stuff(player);
4421 } // create new player
4424 void Server::handlePeerChange(PeerChange &c)
4426 JMutexAutoLock envlock(m_env_mutex);
4427 JMutexAutoLock conlock(m_con_mutex);
4429 if(c.type == PEER_ADDED)
4436 core::map<u16, RemoteClient*>::Node *n;
4437 n = m_clients.find(c.peer_id);
4438 // The client shouldn't already exist
4442 RemoteClient *client = new RemoteClient();
4443 client->peer_id = c.peer_id;
4444 m_clients.insert(client->peer_id, client);
4447 else if(c.type == PEER_REMOVED)
4454 core::map<u16, RemoteClient*>::Node *n;
4455 n = m_clients.find(c.peer_id);
4456 // The client should exist
4460 Mark objects to be not known by the client
4462 RemoteClient *client = n->getValue();
4464 for(core::map<u16, bool>::Iterator
4465 i = client->m_known_objects.getIterator();
4466 i.atEnd()==false; i++)
4469 u16 id = i.getNode()->getKey();
4470 ServerActiveObject* obj = m_env.getActiveObject(id);
4472 if(obj && obj->m_known_by_count > 0)
4473 obj->m_known_by_count--;
4476 // Collect information about leaving in chat
4477 std::wstring message;
4479 Player *player = m_env.getPlayer(c.peer_id);
4482 std::wstring name = narrow_to_wide(player->getName());
4485 message += L" left game";
4487 message += L" (timed out)";
4493 m_env.removePlayer(c.peer_id);
4496 // Set player client disconnected
4498 Player *player = m_env.getPlayer(c.peer_id);
4500 player->peer_id = 0;
4504 delete m_clients[c.peer_id];
4505 m_clients.remove(c.peer_id);
4507 // Send player info to all remaining clients
4510 // Send leave chat message to all remaining clients
4511 BroadcastChatMessage(message);
4520 void Server::handlePeerChanges()
4522 while(m_peer_change_queue.size() > 0)
4524 PeerChange c = m_peer_change_queue.pop_front();
4526 dout_server<<"Server: Handling peer change: "
4527 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4530 handlePeerChange(c);
4534 u64 Server::getPlayerPrivs(Player *player)
4538 std::string playername = player->getName();
4539 // Local player gets all privileges regardless of
4540 // what's set on their account.
4541 if(g_settings->get("name") == playername)
4547 return getPlayerAuthPrivs(playername);
4551 void dedicated_server_loop(Server &server, bool &kill)
4553 DSTACK(__FUNCTION_NAME);
4555 dstream<<DTIME<<std::endl;
4556 dstream<<"========================"<<std::endl;
4557 dstream<<"Running dedicated server"<<std::endl;
4558 dstream<<"========================"<<std::endl;
4561 IntervalLimiter m_profiler_interval;
4565 // This is kind of a hack but can be done like this
4566 // because server.step() is very light
4568 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4573 if(server.getShutdownRequested() || kill)
4575 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4582 float profiler_print_interval =
4583 g_settings->getFloat("profiler_print_interval");
4584 if(profiler_print_interval != 0)
4586 if(m_profiler_interval.step(0.030, profiler_print_interval))
4588 dstream<<"Profiler:"<<std::endl;
4589 g_profiler->print(dstream);
4590 g_profiler->clear();
4597 static int counter = 0;
4603 core::list<PlayerInfo> list = server.getPlayerInfo();
4604 core::list<PlayerInfo>::Iterator i;
4605 static u32 sum_old = 0;
4606 u32 sum = PIChecksum(list);
4609 dstream<<DTIME<<"Player info:"<<std::endl;
4610 for(i=list.begin(); i!=list.end(); i++)
4612 i->PrintLine(&dstream);