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)
1144 JMutexAutoLock envlock(m_env_mutex);
1149 dstream<<"Server: Saving players"<<std::endl;
1150 m_env.serializePlayers(m_mapsavedir);
1153 Save environment metadata
1155 dstream<<"Server: Saving environment metadata"<<std::endl;
1156 m_env.saveMeta(m_mapsavedir);
1168 JMutexAutoLock clientslock(m_con_mutex);
1170 for(core::map<u16, RemoteClient*>::Iterator
1171 i = m_clients.getIterator();
1172 i.atEnd() == false; i++)
1175 // NOTE: These are removed by env destructor
1177 u16 peer_id = i.getNode()->getKey();
1178 JMutexAutoLock envlock(m_env_mutex);
1179 m_env.removePlayer(peer_id);
1183 delete i.getNode()->getValue();
1188 void Server::start(unsigned short port)
1190 DSTACK(__FUNCTION_NAME);
1191 // Stop thread if already running
1194 // Initialize connection
1195 m_con.setTimeoutMs(30);
1199 m_thread.setRun(true);
1202 dout_server<<"Server: Started on port "<<port<<std::endl;
1207 DSTACK(__FUNCTION_NAME);
1209 dout_server<<"Server: Stopping and waiting threads"<<std::endl;
1211 // Stop threads (set run=false first so both start stopping)
1212 m_thread.setRun(false);
1213 m_emergethread.setRun(false);
1215 m_emergethread.stop();
1217 dout_server<<"Server: Threads stopped"<<std::endl;
1220 void Server::step(float dtime)
1222 DSTACK(__FUNCTION_NAME);
1227 JMutexAutoLock lock(m_step_dtime_mutex);
1228 m_step_dtime += dtime;
1232 void Server::AsyncRunStep()
1234 DSTACK(__FUNCTION_NAME);
1238 JMutexAutoLock lock1(m_step_dtime_mutex);
1239 dtime = m_step_dtime;
1243 ScopeProfiler sp(g_profiler, "Server: selecting and sending "
1244 "blocks to clients");
1245 // Send blocks to clients
1252 //dstream<<"Server steps "<<dtime<<std::endl;
1253 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1256 JMutexAutoLock lock1(m_step_dtime_mutex);
1257 m_step_dtime -= dtime;
1264 m_uptime.set(m_uptime.get() + dtime);
1268 // Process connection's timeouts
1269 JMutexAutoLock lock2(m_con_mutex);
1270 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1271 m_con.RunTimeouts(dtime);
1275 // This has to be called so that the client list gets synced
1276 // with the peer list of the connection
1277 ScopeProfiler sp(g_profiler, "Server: peer change handling");
1278 handlePeerChanges();
1282 Update m_time_of_day and overall game time
1285 JMutexAutoLock envlock(m_env_mutex);
1287 m_time_counter += dtime;
1288 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1289 u32 units = (u32)(m_time_counter*speed);
1290 m_time_counter -= (f32)units / speed;
1292 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1294 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1297 Send to clients at constant intervals
1300 m_time_of_day_send_timer -= dtime;
1301 if(m_time_of_day_send_timer < 0.0)
1303 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1305 //JMutexAutoLock envlock(m_env_mutex);
1306 JMutexAutoLock conlock(m_con_mutex);
1308 for(core::map<u16, RemoteClient*>::Iterator
1309 i = m_clients.getIterator();
1310 i.atEnd() == false; i++)
1312 RemoteClient *client = i.getNode()->getValue();
1313 //Player *player = m_env.getPlayer(client->peer_id);
1315 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1316 m_env.getTimeOfDay());
1318 m_con.Send(client->peer_id, 0, data, true);
1324 JMutexAutoLock lock(m_env_mutex);
1326 ScopeProfiler sp(g_profiler, "Server: environment step");
1330 const float map_timer_and_unload_dtime = 5.15;
1331 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1333 JMutexAutoLock lock(m_env_mutex);
1334 // Run Map's timers and unload unused data
1335 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1336 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1337 g_settings->getFloat("server_unload_unused_data_timeout"));
1347 m_liquid_transform_timer += dtime;
1348 if(m_liquid_transform_timer >= 1.00)
1350 m_liquid_transform_timer -= 1.00;
1352 JMutexAutoLock lock(m_env_mutex);
1354 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1356 core::map<v3s16, MapBlock*> modified_blocks;
1357 m_env.getMap().transformLiquids(modified_blocks);
1362 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1363 ServerMap &map = ((ServerMap&)m_env.getMap());
1364 map.updateLighting(modified_blocks, lighting_modified_blocks);
1366 // Add blocks modified by lighting to modified_blocks
1367 for(core::map<v3s16, MapBlock*>::Iterator
1368 i = lighting_modified_blocks.getIterator();
1369 i.atEnd() == false; i++)
1371 MapBlock *block = i.getNode()->getValue();
1372 modified_blocks.insert(block->getPos(), block);
1376 Set the modified blocks unsent for all the clients
1379 JMutexAutoLock lock2(m_con_mutex);
1381 for(core::map<u16, RemoteClient*>::Iterator
1382 i = m_clients.getIterator();
1383 i.atEnd() == false; i++)
1385 RemoteClient *client = i.getNode()->getValue();
1387 if(modified_blocks.size() > 0)
1389 // Remove block from sent history
1390 client->SetBlocksNotSent(modified_blocks);
1395 // Periodically print some info
1397 float &counter = m_print_info_timer;
1403 JMutexAutoLock lock2(m_con_mutex);
1405 for(core::map<u16, RemoteClient*>::Iterator
1406 i = m_clients.getIterator();
1407 i.atEnd() == false; i++)
1409 //u16 peer_id = i.getNode()->getKey();
1410 RemoteClient *client = i.getNode()->getValue();
1411 Player *player = m_env.getPlayer(client->peer_id);
1414 std::cout<<player->getName()<<"\t";
1415 client->PrintInfo(std::cout);
1420 //if(g_settings->getBool("enable_experimental"))
1424 Check added and deleted active objects
1427 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1428 JMutexAutoLock envlock(m_env_mutex);
1429 JMutexAutoLock conlock(m_con_mutex);
1431 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objects");
1433 // Radius inside which objects are active
1436 for(core::map<u16, RemoteClient*>::Iterator
1437 i = m_clients.getIterator();
1438 i.atEnd() == false; i++)
1440 RemoteClient *client = i.getNode()->getValue();
1441 Player *player = m_env.getPlayer(client->peer_id);
1444 // This can happen if the client timeouts somehow
1445 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1447 <<" has no associated player"<<std::endl;*/
1450 v3s16 pos = floatToInt(player->getPosition(), BS);
1452 core::map<u16, bool> removed_objects;
1453 core::map<u16, bool> added_objects;
1454 m_env.getRemovedActiveObjects(pos, radius,
1455 client->m_known_objects, removed_objects);
1456 m_env.getAddedActiveObjects(pos, radius,
1457 client->m_known_objects, added_objects);
1459 // Ignore if nothing happened
1460 if(removed_objects.size() == 0 && added_objects.size() == 0)
1462 //dstream<<"INFO: active objects: none changed"<<std::endl;
1466 std::string data_buffer;
1470 // Handle removed objects
1471 writeU16((u8*)buf, removed_objects.size());
1472 data_buffer.append(buf, 2);
1473 for(core::map<u16, bool>::Iterator
1474 i = removed_objects.getIterator();
1475 i.atEnd()==false; i++)
1478 u16 id = i.getNode()->getKey();
1479 ServerActiveObject* obj = m_env.getActiveObject(id);
1481 // Add to data buffer for sending
1482 writeU16((u8*)buf, i.getNode()->getKey());
1483 data_buffer.append(buf, 2);
1485 // Remove from known objects
1486 client->m_known_objects.remove(i.getNode()->getKey());
1488 if(obj && obj->m_known_by_count > 0)
1489 obj->m_known_by_count--;
1492 // Handle added objects
1493 writeU16((u8*)buf, added_objects.size());
1494 data_buffer.append(buf, 2);
1495 for(core::map<u16, bool>::Iterator
1496 i = added_objects.getIterator();
1497 i.atEnd()==false; i++)
1500 u16 id = i.getNode()->getKey();
1501 ServerActiveObject* obj = m_env.getActiveObject(id);
1504 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1506 dstream<<"WARNING: "<<__FUNCTION_NAME
1507 <<": NULL object"<<std::endl;
1509 type = obj->getType();
1511 // Add to data buffer for sending
1512 writeU16((u8*)buf, id);
1513 data_buffer.append(buf, 2);
1514 writeU8((u8*)buf, type);
1515 data_buffer.append(buf, 1);
1518 data_buffer.append(serializeLongString(
1519 obj->getClientInitializationData()));
1521 data_buffer.append(serializeLongString(""));
1523 // Add to known objects
1524 client->m_known_objects.insert(i.getNode()->getKey(), false);
1527 obj->m_known_by_count++;
1531 SharedBuffer<u8> reply(2 + data_buffer.size());
1532 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1533 memcpy((char*)&reply[2], data_buffer.c_str(),
1534 data_buffer.size());
1536 m_con.Send(client->peer_id, 0, reply, true);
1538 dstream<<"INFO: Server: Sent object remove/add: "
1539 <<removed_objects.size()<<" removed, "
1540 <<added_objects.size()<<" added, "
1541 <<"packet size is "<<reply.getSize()<<std::endl;
1546 Collect a list of all the objects known by the clients
1547 and report it back to the environment.
1550 core::map<u16, bool> all_known_objects;
1552 for(core::map<u16, RemoteClient*>::Iterator
1553 i = m_clients.getIterator();
1554 i.atEnd() == false; i++)
1556 RemoteClient *client = i.getNode()->getValue();
1557 // Go through all known objects of client
1558 for(core::map<u16, bool>::Iterator
1559 i = client->m_known_objects.getIterator();
1560 i.atEnd()==false; i++)
1562 u16 id = i.getNode()->getKey();
1563 all_known_objects[id] = true;
1567 m_env.setKnownActiveObjects(whatever);
1573 Send object messages
1576 JMutexAutoLock envlock(m_env_mutex);
1577 JMutexAutoLock conlock(m_con_mutex);
1579 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1582 // Value = data sent by object
1583 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1585 // Get active object messages from environment
1588 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1592 core::list<ActiveObjectMessage>* message_list = NULL;
1593 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1594 n = buffered_messages.find(aom.id);
1597 message_list = new core::list<ActiveObjectMessage>;
1598 buffered_messages.insert(aom.id, message_list);
1602 message_list = n->getValue();
1604 message_list->push_back(aom);
1607 // Route data to every client
1608 for(core::map<u16, RemoteClient*>::Iterator
1609 i = m_clients.getIterator();
1610 i.atEnd()==false; i++)
1612 RemoteClient *client = i.getNode()->getValue();
1613 std::string reliable_data;
1614 std::string unreliable_data;
1615 // Go through all objects in message buffer
1616 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1617 j = buffered_messages.getIterator();
1618 j.atEnd()==false; j++)
1620 // If object is not known by client, skip it
1621 u16 id = j.getNode()->getKey();
1622 if(client->m_known_objects.find(id) == NULL)
1624 // Get message list of object
1625 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1626 // Go through every message
1627 for(core::list<ActiveObjectMessage>::Iterator
1628 k = list->begin(); k != list->end(); k++)
1630 // Compose the full new data with header
1631 ActiveObjectMessage aom = *k;
1632 std::string new_data;
1635 writeU16((u8*)&buf[0], aom.id);
1636 new_data.append(buf, 2);
1638 new_data += serializeString(aom.datastring);
1639 // Add data to buffer
1641 reliable_data += new_data;
1643 unreliable_data += new_data;
1647 reliable_data and unreliable_data are now ready.
1650 if(reliable_data.size() > 0)
1652 SharedBuffer<u8> reply(2 + reliable_data.size());
1653 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1654 memcpy((char*)&reply[2], reliable_data.c_str(),
1655 reliable_data.size());
1657 m_con.Send(client->peer_id, 0, reply, true);
1659 if(unreliable_data.size() > 0)
1661 SharedBuffer<u8> reply(2 + unreliable_data.size());
1662 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1663 memcpy((char*)&reply[2], unreliable_data.c_str(),
1664 unreliable_data.size());
1665 // Send as unreliable
1666 m_con.Send(client->peer_id, 0, reply, false);
1669 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1671 dstream<<"INFO: Server: Size of object message data: "
1672 <<"reliable: "<<reliable_data.size()
1673 <<", unreliable: "<<unreliable_data.size()
1678 // Clear buffered_messages
1679 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1680 i = buffered_messages.getIterator();
1681 i.atEnd()==false; i++)
1683 delete i.getNode()->getValue();
1687 } // enable_experimental
1690 Send queued-for-sending map edit events.
1693 // Don't send too many at a time
1696 // Single change sending is disabled if queue size is not small
1697 bool disable_single_change_sending = false;
1698 if(m_unsent_map_edit_queue.size() >= 4)
1699 disable_single_change_sending = true;
1701 bool got_any_events = false;
1703 // We'll log the amount of each
1706 while(m_unsent_map_edit_queue.size() != 0)
1708 got_any_events = true;
1710 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1712 // Players far away from the change are stored here.
1713 // Instead of sending the changes, MapBlocks are set not sent
1715 core::list<u16> far_players;
1717 if(event->type == MEET_ADDNODE)
1719 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1720 prof.add("MEET_ADDNODE", 1);
1721 if(disable_single_change_sending)
1722 sendAddNode(event->p, event->n, event->already_known_by_peer,
1725 sendAddNode(event->p, event->n, event->already_known_by_peer,
1728 else if(event->type == MEET_REMOVENODE)
1730 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1731 prof.add("MEET_REMOVENODE", 1);
1732 if(disable_single_change_sending)
1733 sendRemoveNode(event->p, event->already_known_by_peer,
1736 sendRemoveNode(event->p, event->already_known_by_peer,
1739 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1741 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1742 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1743 setBlockNotSent(event->p);
1745 else if(event->type == MEET_OTHER)
1747 dstream<<"Server: MEET_OTHER"<<std::endl;
1748 prof.add("MEET_OTHER", 1);
1749 for(core::map<v3s16, bool>::Iterator
1750 i = event->modified_blocks.getIterator();
1751 i.atEnd()==false; i++)
1753 v3s16 p = i.getNode()->getKey();
1759 prof.add("unknown", 1);
1760 dstream<<"WARNING: Server: Unknown MapEditEvent "
1761 <<((u32)event->type)<<std::endl;
1765 Set blocks not sent to far players
1767 if(far_players.size() > 0)
1769 // Convert list format to that wanted by SetBlocksNotSent
1770 core::map<v3s16, MapBlock*> modified_blocks2;
1771 for(core::map<v3s16, bool>::Iterator
1772 i = event->modified_blocks.getIterator();
1773 i.atEnd()==false; i++)
1775 v3s16 p = i.getNode()->getKey();
1776 modified_blocks2.insert(p,
1777 m_env.getMap().getBlockNoCreateNoEx(p));
1779 // Set blocks not sent
1780 for(core::list<u16>::Iterator
1781 i = far_players.begin();
1782 i != far_players.end(); i++)
1785 RemoteClient *client = getClient(peer_id);
1788 client->SetBlocksNotSent(modified_blocks2);
1794 /*// Don't send too many at a time
1796 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1802 dstream<<"Server: MapEditEvents:"<<std::endl;
1803 prof.print(dstream);
1809 Send object positions
1810 TODO: Get rid of MapBlockObjects
1813 float &counter = m_objectdata_timer;
1815 if(counter >= g_settings->getFloat("objectdata_interval"))
1817 JMutexAutoLock lock1(m_env_mutex);
1818 JMutexAutoLock lock2(m_con_mutex);
1820 ScopeProfiler sp(g_profiler, "Server: sending mbo positions");
1822 SendObjectData(counter);
1829 Trigger emergethread (it somehow gets to a non-triggered but
1830 bysy state sometimes)
1833 float &counter = m_emergethread_trigger_timer;
1839 m_emergethread.trigger();
1843 // Save map, players and auth stuff
1845 float &counter = m_savemap_timer;
1847 if(counter >= g_settings->getFloat("server_map_save_interval"))
1851 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1854 if(m_authmanager.isModified())
1855 m_authmanager.save();
1858 if(m_banmanager.isModified())
1859 m_banmanager.save();
1862 JMutexAutoLock lock(m_env_mutex);
1864 /*// Unload unused data (delete from memory)
1865 m_env.getMap().unloadUnusedData(
1866 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1868 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1869 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1872 // Save only changed parts
1873 m_env.getMap().save(true);
1875 /*if(deleted_count > 0)
1877 dout_server<<"Server: Unloaded "<<deleted_count
1878 <<" blocks from memory"<<std::endl;
1882 m_env.serializePlayers(m_mapsavedir);
1884 // Save environment metadata
1885 m_env.saveMeta(m_mapsavedir);
1890 void Server::Receive()
1892 DSTACK(__FUNCTION_NAME);
1893 u32 data_maxsize = 10000;
1894 Buffer<u8> data(data_maxsize);
1899 JMutexAutoLock conlock(m_con_mutex);
1900 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1903 // This has to be called so that the client list gets synced
1904 // with the peer list of the connection
1905 handlePeerChanges();
1907 ProcessData(*data, datasize, peer_id);
1909 catch(con::InvalidIncomingDataException &e)
1911 derr_server<<"Server::Receive(): "
1912 "InvalidIncomingDataException: what()="
1913 <<e.what()<<std::endl;
1915 catch(con::PeerNotFoundException &e)
1917 //NOTE: This is not needed anymore
1919 // The peer has been disconnected.
1920 // Find the associated player and remove it.
1922 /*JMutexAutoLock envlock(m_env_mutex);
1924 dout_server<<"ServerThread: peer_id="<<peer_id
1925 <<" has apparently closed connection. "
1926 <<"Removing player."<<std::endl;
1928 m_env.removePlayer(peer_id);*/
1932 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1934 DSTACK(__FUNCTION_NAME);
1935 // Environment is locked first.
1936 JMutexAutoLock envlock(m_env_mutex);
1937 JMutexAutoLock conlock(m_con_mutex);
1941 peer = m_con.GetPeer(peer_id);
1943 catch(con::PeerNotFoundException &e)
1945 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1946 <<peer_id<<" not found"<<std::endl;
1950 // drop player if is ip is banned
1951 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1952 SendAccessDenied(m_con, peer_id,
1953 L"Your ip is banned. Banned name was "
1954 +narrow_to_wide(m_banmanager.getBanName(
1955 peer->address.serializeString())));
1956 m_con.deletePeer(peer_id, false);
1960 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1968 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1970 if(command == TOSERVER_INIT)
1972 // [0] u16 TOSERVER_INIT
1973 // [2] u8 SER_FMT_VER_HIGHEST
1974 // [3] u8[20] player_name
1975 // [23] u8[28] password <--- can be sent without this, from old versions
1977 if(datasize < 2+1+PLAYERNAME_SIZE)
1980 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1981 <<peer->id<<std::endl;
1983 // First byte after command is maximum supported
1984 // serialization version
1985 u8 client_max = data[2];
1986 u8 our_max = SER_FMT_VER_HIGHEST;
1987 // Use the highest version supported by both
1988 u8 deployed = core::min_(client_max, our_max);
1989 // If it's lower than the lowest supported, give up.
1990 if(deployed < SER_FMT_VER_LOWEST)
1991 deployed = SER_FMT_VER_INVALID;
1993 //peer->serialization_version = deployed;
1994 getClient(peer->id)->pending_serialization_version = deployed;
1996 if(deployed == SER_FMT_VER_INVALID)
1998 derr_server<<DTIME<<"Server: Cannot negotiate "
1999 "serialization version with peer "
2000 <<peer_id<<std::endl;
2001 SendAccessDenied(m_con, peer_id,
2002 L"Your client is too old (map format)");
2007 Read and check network protocol version
2010 u16 net_proto_version = 0;
2011 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2013 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2016 getClient(peer->id)->net_proto_version = net_proto_version;
2018 if(net_proto_version == 0)
2020 SendAccessDenied(m_con, peer_id,
2021 L"Your client is too old. Please upgrade.");
2025 /* Uhh... this should actually be a warning but let's do it like this */
2026 if(net_proto_version < 2)
2028 SendAccessDenied(m_con, peer_id,
2029 L"Your client is too old. Please upgrade.");
2038 char playername[PLAYERNAME_SIZE];
2039 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2041 playername[i] = data[3+i];
2043 playername[PLAYERNAME_SIZE-1] = 0;
2045 if(playername[0]=='\0')
2047 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2048 SendAccessDenied(m_con, peer_id,
2053 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2055 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2056 SendAccessDenied(m_con, peer_id,
2057 L"Name contains unallowed characters");
2062 char password[PASSWORD_SIZE];
2063 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2065 // old version - assume blank password
2070 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2072 password[i] = data[23+i];
2074 password[PASSWORD_SIZE-1] = 0;
2077 std::string checkpwd;
2078 if(m_authmanager.exists(playername))
2080 checkpwd = m_authmanager.getPassword(playername);
2084 checkpwd = g_settings->get("default_password");
2087 /*dstream<<"Server: Client gave password '"<<password
2088 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2090 if(password != checkpwd && m_authmanager.exists(playername))
2092 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2093 <<": supplied invalid password for "
2094 <<playername<<std::endl;
2095 SendAccessDenied(m_con, peer_id, L"Invalid password");
2099 // Add player to auth manager
2100 if(m_authmanager.exists(playername) == false)
2102 derr_server<<DTIME<<"Server: adding player "<<playername
2103 <<" to auth manager"<<std::endl;
2104 m_authmanager.add(playername);
2105 m_authmanager.setPassword(playername, checkpwd);
2106 m_authmanager.setPrivs(playername,
2107 stringToPrivs(g_settings->get("default_privs")));
2108 m_authmanager.save();
2111 // Enforce user limit.
2112 // Don't enforce for users that have some admin right
2113 if(m_clients.size() >= g_settings->getU16("max_users") &&
2114 (m_authmanager.getPrivs(playername)
2115 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2116 playername != g_settings->get("name"))
2118 SendAccessDenied(m_con, peer_id, L"Too many users.");
2123 Player *player = emergePlayer(playername, password, peer_id);
2126 // DEBUG: Test serialization
2127 std::ostringstream test_os;
2128 player->serialize(test_os);
2129 dstream<<"Player serialization test: \""<<test_os.str()
2131 std::istringstream test_is(test_os.str());
2132 player->deSerialize(test_is);
2135 // If failed, cancel
2138 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2139 <<": failed to emerge player"<<std::endl;
2144 // If a client is already connected to the player, cancel
2145 if(player->peer_id != 0)
2147 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2148 <<" tried to connect to "
2149 "an already connected player (peer_id="
2150 <<player->peer_id<<")"<<std::endl;
2153 // Set client of player
2154 player->peer_id = peer_id;
2157 // Check if player doesn't exist
2159 throw con::InvalidIncomingDataException
2160 ("Server::ProcessData(): INIT: Player doesn't exist");
2162 /*// update name if it was supplied
2163 if(datasize >= 20+3)
2166 player->updateName((const char*)&data[3]);
2170 Answer with a TOCLIENT_INIT
2173 SharedBuffer<u8> reply(2+1+6+8);
2174 writeU16(&reply[0], TOCLIENT_INIT);
2175 writeU8(&reply[2], deployed);
2176 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2177 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2180 m_con.Send(peer_id, 0, reply, true);
2184 Send complete position information
2186 SendMovePlayer(player);
2191 if(command == TOSERVER_INIT2)
2193 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2194 <<peer->id<<std::endl;
2197 getClient(peer->id)->serialization_version
2198 = getClient(peer->id)->pending_serialization_version;
2201 Send some initialization data
2204 // Send player info to all players
2207 // Send inventory to player
2208 UpdateCrafting(peer->id);
2209 SendInventory(peer->id);
2211 // Send player items to all players
2216 Player *player = m_env.getPlayer(peer_id);
2217 SendPlayerHP(player);
2222 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2223 m_env.getTimeOfDay());
2224 m_con.Send(peer->id, 0, data, true);
2227 // Send information about server to player in chat
2228 SendChatMessage(peer_id, getStatusString());
2230 // Send information about joining in chat
2232 std::wstring name = L"unknown";
2233 Player *player = m_env.getPlayer(peer_id);
2235 name = narrow_to_wide(player->getName());
2237 std::wstring message;
2240 message += L" joined game";
2241 BroadcastChatMessage(message);
2244 // Warnings about protocol version can be issued here
2245 /*if(getClient(peer->id)->net_proto_version == 0)
2247 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2253 if(peer_ser_ver == SER_FMT_VER_INVALID)
2255 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2256 " serialization format invalid or not initialized."
2257 " Skipping incoming command="<<command<<std::endl;
2261 Player *player = m_env.getPlayer(peer_id);
2264 derr_server<<"Server::ProcessData(): Cancelling: "
2265 "No player for peer_id="<<peer_id
2269 if(command == TOSERVER_PLAYERPOS)
2271 if(datasize < 2+12+12+4+4)
2275 v3s32 ps = readV3S32(&data[start+2]);
2276 v3s32 ss = readV3S32(&data[start+2+12]);
2277 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2278 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2279 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2280 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2281 pitch = wrapDegrees(pitch);
2282 yaw = wrapDegrees(yaw);
2283 player->setPosition(position);
2284 player->setSpeed(speed);
2285 player->setPitch(pitch);
2286 player->setYaw(yaw);
2288 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2289 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2290 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2292 else if(command == TOSERVER_GOTBLOCKS)
2305 u16 count = data[2];
2306 for(u16 i=0; i<count; i++)
2308 if((s16)datasize < 2+1+(i+1)*6)
2309 throw con::InvalidIncomingDataException
2310 ("GOTBLOCKS length is too short");
2311 v3s16 p = readV3S16(&data[2+1+i*6]);
2312 /*dstream<<"Server: GOTBLOCKS ("
2313 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2314 RemoteClient *client = getClient(peer_id);
2315 client->GotBlock(p);
2318 else if(command == TOSERVER_DELETEDBLOCKS)
2331 u16 count = data[2];
2332 for(u16 i=0; i<count; i++)
2334 if((s16)datasize < 2+1+(i+1)*6)
2335 throw con::InvalidIncomingDataException
2336 ("DELETEDBLOCKS length is too short");
2337 v3s16 p = readV3S16(&data[2+1+i*6]);
2338 /*dstream<<"Server: DELETEDBLOCKS ("
2339 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2340 RemoteClient *client = getClient(peer_id);
2341 client->SetBlockNotSent(p);
2344 else if(command == TOSERVER_CLICK_OBJECT)
2349 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2354 [2] u8 button (0=left, 1=right)
2359 u8 button = readU8(&data[2]);
2361 p.X = readS16(&data[3]);
2362 p.Y = readS16(&data[5]);
2363 p.Z = readS16(&data[7]);
2364 s16 id = readS16(&data[9]);
2365 //u16 item_i = readU16(&data[11]);
2367 MapBlock *block = NULL;
2370 block = m_env.getMap().getBlockNoCreate(p);
2372 catch(InvalidPositionException &e)
2374 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2378 MapBlockObject *obj = block->getObject(id);
2382 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2386 //TODO: Check that object is reasonably close
2391 InventoryList *ilist = player->inventory.getList("main");
2392 if(g_settings->getBool("creative_mode") == false && ilist != NULL)
2395 // Skip if inventory has no free space
2396 if(ilist->getUsedSlots() == ilist->getSize())
2398 dout_server<<"Player inventory has no free space"<<std::endl;
2403 Create the inventory item
2405 InventoryItem *item = NULL;
2406 // If it is an item-object, take the item from it
2407 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2409 item = ((ItemObject*)obj)->createInventoryItem();
2411 // Else create an item of the object
2414 item = new MapBlockObjectItem
2415 (obj->getInventoryString());
2418 // Add to inventory and send inventory
2419 ilist->addItem(item);
2420 UpdateCrafting(player->peer_id);
2421 SendInventory(player->peer_id);
2424 // Remove from block
2425 block->removeObject(id);
2428 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2433 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2439 [2] u8 button (0=left, 1=right)
2443 u8 button = readU8(&data[2]);
2444 u16 id = readS16(&data[3]);
2445 u16 item_i = readU16(&data[11]);
2447 ServerActiveObject *obj = m_env.getActiveObject(id);
2451 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2456 // Skip if object has been removed
2460 //TODO: Check that object is reasonably close
2462 // Left click, pick object up (usually)
2466 Try creating inventory item
2468 InventoryItem *item = obj->createPickedUpItem();
2472 InventoryList *ilist = player->inventory.getList("main");
2475 if(g_settings->getBool("creative_mode") == false)
2477 // Skip if inventory has no free space
2478 if(ilist->roomForItem(item) == false)
2480 dout_server<<"Player inventory has no free space"<<std::endl;
2484 // Add to inventory and send inventory
2485 ilist->addItem(item);
2486 UpdateCrafting(player->peer_id);
2487 SendInventory(player->peer_id);
2490 // Remove object from environment
2491 obj->m_removed = true;
2497 Item cannot be picked up. Punch it instead.
2500 ToolItem *titem = NULL;
2501 std::string toolname = "";
2503 InventoryList *mlist = player->inventory.getList("main");
2506 InventoryItem *item = mlist->getItem(item_i);
2507 if(item && (std::string)item->getName() == "ToolItem")
2509 titem = (ToolItem*)item;
2510 toolname = titem->getToolName();
2514 v3f playerpos = player->getPosition();
2515 v3f objpos = obj->getBasePosition();
2516 v3f dir = (objpos - playerpos).normalize();
2518 u16 wear = obj->punch(toolname, dir);
2522 bool weared_out = titem->addWear(wear);
2524 mlist->deleteItem(item_i);
2525 SendInventory(player->peer_id);
2529 // Right click, do something with object
2532 // Track hp changes super-crappily
2533 u16 oldhp = player->hp;
2536 obj->rightClick(player);
2539 if(player->hp != oldhp)
2541 SendPlayerHP(player);
2545 else if(command == TOSERVER_GROUND_ACTION)
2553 [3] v3s16 nodepos_undersurface
2554 [9] v3s16 nodepos_abovesurface
2559 2: stop digging (all parameters ignored)
2560 3: digging completed
2562 u8 action = readU8(&data[2]);
2564 p_under.X = readS16(&data[3]);
2565 p_under.Y = readS16(&data[5]);
2566 p_under.Z = readS16(&data[7]);
2568 p_over.X = readS16(&data[9]);
2569 p_over.Y = readS16(&data[11]);
2570 p_over.Z = readS16(&data[13]);
2571 u16 item_i = readU16(&data[15]);
2573 //TODO: Check that target is reasonably close
2581 NOTE: This can be used in the future to check if
2582 somebody is cheating, by checking the timing.
2589 else if(action == 2)
2592 RemoteClient *client = getClient(peer->id);
2593 JMutexAutoLock digmutex(client->m_dig_mutex);
2594 client->m_dig_tool_item = -1;
2599 3: Digging completed
2601 else if(action == 3)
2603 // Mandatory parameter; actually used for nothing
2604 core::map<v3s16, MapBlock*> modified_blocks;
2606 content_t material = CONTENT_IGNORE;
2607 u8 mineral = MINERAL_NONE;
2609 bool cannot_remove_node = false;
2613 MapNode n = m_env.getMap().getNode(p_under);
2615 mineral = n.getMineral();
2616 // Get material at position
2617 material = n.getContent();
2618 // If not yet cancelled
2619 if(cannot_remove_node == false)
2621 // If it's not diggable, do nothing
2622 if(content_diggable(material) == false)
2624 derr_server<<"Server: Not finishing digging: "
2625 <<"Node not diggable"
2627 cannot_remove_node = true;
2630 // If not yet cancelled
2631 if(cannot_remove_node == false)
2633 // Get node metadata
2634 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2635 if(meta && meta->nodeRemovalDisabled() == true)
2637 derr_server<<"Server: Not finishing digging: "
2638 <<"Node metadata disables removal"
2640 cannot_remove_node = true;
2644 catch(InvalidPositionException &e)
2646 derr_server<<"Server: Not finishing digging: Node not found."
2647 <<" Adding block to emerge queue."
2649 m_emerge_queue.addBlock(peer_id,
2650 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2651 cannot_remove_node = true;
2654 // Make sure the player is allowed to do it
2655 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2657 dstream<<"Player "<<player->getName()<<" cannot remove node"
2658 <<" because privileges are "<<getPlayerPrivs(player)
2660 cannot_remove_node = true;
2664 If node can't be removed, set block to be re-sent to
2667 if(cannot_remove_node)
2669 derr_server<<"Server: Not finishing digging."<<std::endl;
2671 // Client probably has wrong data.
2672 // Set block not sent, so that client will get
2674 dstream<<"Client "<<peer_id<<" tried to dig "
2675 <<"node; but node cannot be removed."
2676 <<" setting MapBlock not sent."<<std::endl;
2677 RemoteClient *client = getClient(peer_id);
2678 v3s16 blockpos = getNodeBlockPos(p_under);
2679 client->SetBlockNotSent(blockpos);
2685 Send the removal to all close-by players.
2686 - If other player is close, send REMOVENODE
2687 - Otherwise set blocks not sent
2689 core::list<u16> far_players;
2690 sendRemoveNode(p_under, peer_id, &far_players, 30);
2693 Update and send inventory
2696 if(g_settings->getBool("creative_mode") == false)
2701 InventoryList *mlist = player->inventory.getList("main");
2704 InventoryItem *item = mlist->getItem(item_i);
2705 if(item && (std::string)item->getName() == "ToolItem")
2707 ToolItem *titem = (ToolItem*)item;
2708 std::string toolname = titem->getToolName();
2710 // Get digging properties for material and tool
2711 DiggingProperties prop =
2712 getDiggingProperties(material, toolname);
2714 if(prop.diggable == false)
2716 derr_server<<"Server: WARNING: Player digged"
2717 <<" with impossible material + tool"
2718 <<" combination"<<std::endl;
2721 bool weared_out = titem->addWear(prop.wear);
2725 mlist->deleteItem(item_i);
2731 Add dug item to inventory
2734 InventoryItem *item = NULL;
2736 if(mineral != MINERAL_NONE)
2737 item = getDiggedMineralItem(mineral);
2742 std::string &dug_s = content_features(material).dug_item;
2745 std::istringstream is(dug_s, std::ios::binary);
2746 item = InventoryItem::deSerialize(is);
2752 // Add a item to inventory
2753 player->inventory.addItem("main", item);
2756 UpdateCrafting(player->peer_id);
2757 SendInventory(player->peer_id);
2762 if(mineral != MINERAL_NONE)
2763 item = getDiggedMineralItem(mineral);
2768 std::string &extra_dug_s = content_features(material).extra_dug_item;
2769 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2770 if(extra_dug_s != "" && extra_rarity != 0
2771 && myrand() % extra_rarity == 0)
2773 std::istringstream is(extra_dug_s, std::ios::binary);
2774 item = InventoryItem::deSerialize(is);
2780 // Add a item to inventory
2781 player->inventory.addItem("main", item);
2784 UpdateCrafting(player->peer_id);
2785 SendInventory(player->peer_id);
2791 (this takes some time so it is done after the quick stuff)
2794 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2796 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2799 Set blocks not sent to far players
2801 for(core::list<u16>::Iterator
2802 i = far_players.begin();
2803 i != far_players.end(); i++)
2806 RemoteClient *client = getClient(peer_id);
2809 client->SetBlocksNotSent(modified_blocks);
2816 else if(action == 1)
2819 InventoryList *ilist = player->inventory.getList("main");
2824 InventoryItem *item = ilist->getItem(item_i);
2826 // If there is no item, it is not possible to add it anywhere
2831 Handle material items
2833 if(std::string("MaterialItem") == item->getName())
2836 // Don't add a node if this is not a free space
2837 MapNode n2 = m_env.getMap().getNode(p_over);
2838 bool no_enough_privs =
2839 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2841 dstream<<"Player "<<player->getName()<<" cannot add node"
2842 <<" because privileges are "<<getPlayerPrivs(player)
2845 if(content_features(n2).buildable_to == false
2848 // Client probably has wrong data.
2849 // Set block not sent, so that client will get
2851 dstream<<"Client "<<peer_id<<" tried to place"
2852 <<" node in invalid position; setting"
2853 <<" MapBlock not sent."<<std::endl;
2854 RemoteClient *client = getClient(peer_id);
2855 v3s16 blockpos = getNodeBlockPos(p_over);
2856 client->SetBlockNotSent(blockpos);
2860 catch(InvalidPositionException &e)
2862 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2863 <<" Adding block to emerge queue."
2865 m_emerge_queue.addBlock(peer_id,
2866 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2870 // Reset build time counter
2871 getClient(peer->id)->m_time_from_building = 0.0;
2874 MaterialItem *mitem = (MaterialItem*)item;
2876 n.setContent(mitem->getMaterial());
2878 // Calculate direction for wall mounted stuff
2879 if(content_features(n).wall_mounted)
2880 n.param2 = packDir(p_under - p_over);
2882 // Calculate the direction for furnaces and chests and stuff
2883 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2885 v3f playerpos = player->getPosition();
2886 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2887 blockpos = blockpos.normalize();
2889 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2903 Send to all close-by players
2905 core::list<u16> far_players;
2906 sendAddNode(p_over, n, 0, &far_players, 30);
2911 InventoryList *ilist = player->inventory.getList("main");
2912 if(g_settings->getBool("creative_mode") == false && ilist)
2914 // Remove from inventory and send inventory
2915 if(mitem->getCount() == 1)
2916 ilist->deleteItem(item_i);
2920 UpdateCrafting(peer_id);
2921 SendInventory(peer_id);
2927 This takes some time so it is done after the quick stuff
2929 core::map<v3s16, MapBlock*> modified_blocks;
2931 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2933 std::string p_name = std::string(player->getName());
2934 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2937 Set blocks not sent to far players
2939 for(core::list<u16>::Iterator
2940 i = far_players.begin();
2941 i != far_players.end(); i++)
2944 RemoteClient *client = getClient(peer_id);
2947 client->SetBlocksNotSent(modified_blocks);
2951 Calculate special events
2954 /*if(n.d == CONTENT_MESE)
2957 for(s16 z=-1; z<=1; z++)
2958 for(s16 y=-1; y<=1; y++)
2959 for(s16 x=-1; x<=1; x++)
2966 Place other item (not a block)
2970 v3s16 blockpos = getNodeBlockPos(p_over);
2973 Check that the block is loaded so that the item
2974 can properly be added to the static list too
2976 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2979 derr_server<<"Error while placing object: "
2980 "block not found"<<std::endl;
2985 If in creative mode, item dropping is disabled unless
2986 player has build privileges
2988 if(g_settings->getBool("creative_mode") &&
2989 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2991 derr_server<<"Not allowing player to drop item: "
2992 "creative mode and no build privs"<<std::endl;
2996 dout_server<<"Placing a miscellaneous item on map"
2999 // Calculate a position for it
3000 v3f pos = intToFloat(p_over, BS);
3002 pos.Y -= BS*0.25; // let it drop a bit
3004 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3005 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3010 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
3014 derr_server<<"WARNING: item resulted in NULL object, "
3015 <<"not placing onto map"
3020 // Add the object to the environment
3021 m_env.addActiveObject(obj);
3023 dout_server<<"Placed object"<<std::endl;
3025 if(g_settings->getBool("creative_mode") == false)
3027 // Delete the right amount of items from the slot
3028 u16 dropcount = item->getDropCount();
3030 // Delete item if all gone
3031 if(item->getCount() <= dropcount)
3033 if(item->getCount() < dropcount)
3034 dstream<<"WARNING: Server: dropped more items"
3035 <<" than the slot contains"<<std::endl;
3037 InventoryList *ilist = player->inventory.getList("main");
3039 // Remove from inventory and send inventory
3040 ilist->deleteItem(item_i);
3042 // Else decrement it
3044 item->remove(dropcount);
3047 UpdateCrafting(peer_id);
3048 SendInventory(peer_id);
3056 Catch invalid actions
3060 derr_server<<"WARNING: Server: Invalid action "
3061 <<action<<std::endl;
3065 else if(command == TOSERVER_RELEASE)
3074 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3077 else if(command == TOSERVER_SIGNTEXT)
3079 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3088 std::string datastring((char*)&data[2], datasize-2);
3089 std::istringstream is(datastring, std::ios_base::binary);
3092 is.read((char*)buf, 6);
3093 v3s16 blockpos = readV3S16(buf);
3094 is.read((char*)buf, 2);
3095 s16 id = readS16(buf);
3096 is.read((char*)buf, 2);
3097 u16 textlen = readU16(buf);
3099 for(u16 i=0; i<textlen; i++)
3101 is.read((char*)buf, 1);
3102 text += (char)buf[0];
3105 MapBlock *block = NULL;
3108 block = m_env.getMap().getBlockNoCreate(blockpos);
3110 catch(InvalidPositionException &e)
3112 derr_server<<"Error while setting sign text: "
3113 "block not found"<<std::endl;
3117 MapBlockObject *obj = block->getObject(id);
3120 derr_server<<"Error while setting sign text: "
3121 "object not found"<<std::endl;
3125 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3127 derr_server<<"Error while setting sign text: "
3128 "object is not a sign"<<std::endl;
3132 ((SignObject*)obj)->setText(text);
3134 obj->getBlock()->setChangedFlag();
3136 else if(command == TOSERVER_SIGNNODETEXT)
3138 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3146 std::string datastring((char*)&data[2], datasize-2);
3147 std::istringstream is(datastring, std::ios_base::binary);
3150 is.read((char*)buf, 6);
3151 v3s16 p = readV3S16(buf);
3152 is.read((char*)buf, 2);
3153 u16 textlen = readU16(buf);
3155 for(u16 i=0; i<textlen; i++)
3157 is.read((char*)buf, 1);
3158 text += (char)buf[0];
3161 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3164 if(meta->typeId() != CONTENT_SIGN_WALL)
3166 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3167 signmeta->setText(text);
3169 v3s16 blockpos = getNodeBlockPos(p);
3170 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3173 block->setChangedFlag();
3176 for(core::map<u16, RemoteClient*>::Iterator
3177 i = m_clients.getIterator();
3178 i.atEnd()==false; i++)
3180 RemoteClient *client = i.getNode()->getValue();
3181 client->SetBlockNotSent(blockpos);
3184 else if(command == TOSERVER_INVENTORY_ACTION)
3186 /*// Ignore inventory changes if in creative mode
3187 if(g_settings->getBool("creative_mode") == true)
3189 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3193 // Strip command and create a stream
3194 std::string datastring((char*)&data[2], datasize-2);
3195 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3196 std::istringstream is(datastring, std::ios_base::binary);
3198 InventoryAction *a = InventoryAction::deSerialize(is);
3203 c.current_player = player;
3206 Handle craftresult specially if not in creative mode
3208 bool disable_action = false;
3209 if(a->getType() == IACTION_MOVE
3210 && g_settings->getBool("creative_mode") == false)
3212 IMoveAction *ma = (IMoveAction*)a;
3213 if(ma->to_inv == "current_player" &&
3214 ma->from_inv == "current_player")
3216 InventoryList *rlist = player->inventory.getList("craftresult");
3218 InventoryList *clist = player->inventory.getList("craft");
3220 InventoryList *mlist = player->inventory.getList("main");
3223 Craftresult is no longer preview if something
3226 if(ma->to_list == "craftresult"
3227 && ma->from_list != "craftresult")
3229 // If it currently is a preview, remove
3231 if(player->craftresult_is_preview)
3233 rlist->deleteItem(0);
3235 player->craftresult_is_preview = false;
3238 Crafting takes place if this condition is true.
3240 if(player->craftresult_is_preview &&
3241 ma->from_list == "craftresult")
3243 player->craftresult_is_preview = false;
3244 clist->decrementMaterials(1);
3247 If the craftresult is placed on itself, move it to
3248 main inventory instead of doing the action
3250 if(ma->to_list == "craftresult"
3251 && ma->from_list == "craftresult")
3253 disable_action = true;
3255 InventoryItem *item1 = rlist->changeItem(0, NULL);
3256 mlist->addItem(item1);
3259 // Disallow moving items if not allowed to build
3260 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3264 // if it's a locking chest, only allow the owner or server admins to move items
3265 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3267 Strfnd fn(ma->from_inv);
3268 std::string id0 = fn.next(":");
3269 if(id0 == "nodemeta")
3272 p.X = stoi(fn.next(","));
3273 p.Y = stoi(fn.next(","));
3274 p.Z = stoi(fn.next(","));
3275 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3276 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3277 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3278 if (lcm->getOwner() != player->getName())
3283 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3285 Strfnd fn(ma->to_inv);
3286 std::string id0 = fn.next(":");
3287 if(id0 == "nodemeta")
3290 p.X = stoi(fn.next(","));
3291 p.Y = stoi(fn.next(","));
3292 p.Z = stoi(fn.next(","));
3293 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3294 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3295 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3296 if (lcm->getOwner() != player->getName())
3303 if(disable_action == false)
3305 // Feed action to player inventory
3313 UpdateCrafting(player->peer_id);
3314 SendInventory(player->peer_id);
3319 dstream<<"TOSERVER_INVENTORY_ACTION: "
3320 <<"InventoryAction::deSerialize() returned NULL"
3324 else if(command == TOSERVER_CHAT_MESSAGE)
3332 std::string datastring((char*)&data[2], datasize-2);
3333 std::istringstream is(datastring, std::ios_base::binary);
3336 is.read((char*)buf, 2);
3337 u16 len = readU16(buf);
3339 std::wstring message;
3340 for(u16 i=0; i<len; i++)
3342 is.read((char*)buf, 2);
3343 message += (wchar_t)readU16(buf);
3346 // Get player name of this client
3347 std::wstring name = narrow_to_wide(player->getName());
3349 // Line to send to players
3351 // Whether to send to the player that sent the line
3352 bool send_to_sender = false;
3353 // Whether to send to other players
3354 bool send_to_others = false;
3356 // Local player gets all privileges regardless of
3357 // what's set on their account.
3358 u64 privs = getPlayerPrivs(player);
3361 if(message[0] == L'/')
3363 size_t strip_size = 1;
3364 if (message[1] == L'#') // support old-style commans
3366 message = message.substr(strip_size);
3368 WStrfnd f1(message);
3369 f1.next(L" "); // Skip over /#whatever
3370 std::wstring paramstring = f1.next(L"");
3372 ServerCommandContext *ctx = new ServerCommandContext(
3373 str_split(message, L' '),
3380 std::wstring reply(processServerCommand(ctx));
3381 send_to_sender = ctx->flags & SEND_TO_SENDER;
3382 send_to_others = ctx->flags & SEND_TO_OTHERS;
3384 if (ctx->flags & SEND_NO_PREFIX)
3387 line += L"Server: " + reply;
3394 if(privs & PRIV_SHOUT)
3400 send_to_others = true;
3404 line += L"Server: You are not allowed to shout";
3405 send_to_sender = true;
3411 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3414 Send the message to clients
3416 for(core::map<u16, RemoteClient*>::Iterator
3417 i = m_clients.getIterator();
3418 i.atEnd() == false; i++)
3420 // Get client and check that it is valid
3421 RemoteClient *client = i.getNode()->getValue();
3422 assert(client->peer_id == i.getNode()->getKey());
3423 if(client->serialization_version == SER_FMT_VER_INVALID)
3427 bool sender_selected = (peer_id == client->peer_id);
3428 if(sender_selected == true && send_to_sender == false)
3430 if(sender_selected == false && send_to_others == false)
3433 SendChatMessage(client->peer_id, line);
3437 else if(command == TOSERVER_DAMAGE)
3439 if(g_settings->getBool("enable_damage"))
3441 std::string datastring((char*)&data[2], datasize-2);
3442 std::istringstream is(datastring, std::ios_base::binary);
3443 u8 damage = readU8(is);
3444 if(player->hp > damage)
3446 player->hp -= damage;
3452 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3455 v3f pos = findSpawnPos(m_env.getServerMap());
3456 player->setPosition(pos);
3458 SendMovePlayer(player);
3459 SendPlayerHP(player);
3461 //TODO: Throw items around
3465 SendPlayerHP(player);
3467 else if(command == TOSERVER_PASSWORD)
3470 [0] u16 TOSERVER_PASSWORD
3471 [2] u8[28] old password
3472 [30] u8[28] new password
3475 if(datasize != 2+PASSWORD_SIZE*2)
3477 /*char password[PASSWORD_SIZE];
3478 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3479 password[i] = data[2+i];
3480 password[PASSWORD_SIZE-1] = 0;*/
3482 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3490 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3492 char c = data[2+PASSWORD_SIZE+i];
3498 dstream<<"Server: Client requests a password change from "
3499 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3501 std::string playername = player->getName();
3503 if(m_authmanager.exists(playername) == false)
3505 dstream<<"Server: playername not found in authmanager"<<std::endl;
3506 // Wrong old password supplied!!
3507 SendChatMessage(peer_id, L"playername not found in authmanager");
3511 std::string checkpwd = m_authmanager.getPassword(playername);
3513 if(oldpwd != checkpwd)
3515 dstream<<"Server: invalid old password"<<std::endl;
3516 // Wrong old password supplied!!
3517 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3521 m_authmanager.setPassword(playername, newpwd);
3523 dstream<<"Server: password change successful for "<<playername
3525 SendChatMessage(peer_id, L"Password change successful");
3527 else if (command == TOSERVER_PLAYERITEM)
3532 u16 item = readU16(&data[2]);
3533 player->wieldItem(item);
3534 SendWieldedItem(player);
3538 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3539 "unknown command "<<command<<std::endl;
3543 catch(SendFailedException &e)
3545 derr_server<<"Server::ProcessData(): SendFailedException: "
3551 void Server::onMapEditEvent(MapEditEvent *event)
3553 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3554 if(m_ignore_map_edit_events)
3556 MapEditEvent *e = event->clone();
3557 m_unsent_map_edit_queue.push_back(e);
3560 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3562 if(id == "current_player")
3564 assert(c->current_player);
3565 return &(c->current_player->inventory);
3569 std::string id0 = fn.next(":");
3571 if(id0 == "nodemeta")
3574 p.X = stoi(fn.next(","));
3575 p.Y = stoi(fn.next(","));
3576 p.Z = stoi(fn.next(","));
3577 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3579 return meta->getInventory();
3580 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3581 <<"no metadata found"<<std::endl;
3585 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3588 void Server::inventoryModified(InventoryContext *c, std::string id)
3590 if(id == "current_player")
3592 assert(c->current_player);
3594 UpdateCrafting(c->current_player->peer_id);
3595 SendInventory(c->current_player->peer_id);
3600 std::string id0 = fn.next(":");
3602 if(id0 == "nodemeta")
3605 p.X = stoi(fn.next(","));
3606 p.Y = stoi(fn.next(","));
3607 p.Z = stoi(fn.next(","));
3608 v3s16 blockpos = getNodeBlockPos(p);
3610 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3612 meta->inventoryModified();
3614 for(core::map<u16, RemoteClient*>::Iterator
3615 i = m_clients.getIterator();
3616 i.atEnd()==false; i++)
3618 RemoteClient *client = i.getNode()->getValue();
3619 client->SetBlockNotSent(blockpos);
3625 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3628 core::list<PlayerInfo> Server::getPlayerInfo()
3630 DSTACK(__FUNCTION_NAME);
3631 JMutexAutoLock envlock(m_env_mutex);
3632 JMutexAutoLock conlock(m_con_mutex);
3634 core::list<PlayerInfo> list;
3636 core::list<Player*> players = m_env.getPlayers();
3638 core::list<Player*>::Iterator i;
3639 for(i = players.begin();
3640 i != players.end(); i++)
3644 Player *player = *i;
3647 con::Peer *peer = m_con.GetPeer(player->peer_id);
3648 // Copy info from peer to info struct
3650 info.address = peer->address;
3651 info.avg_rtt = peer->avg_rtt;
3653 catch(con::PeerNotFoundException &e)
3655 // Set dummy peer info
3657 info.address = Address(0,0,0,0,0);
3661 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3662 info.position = player->getPosition();
3664 list.push_back(info);
3671 void Server::peerAdded(con::Peer *peer)
3673 DSTACK(__FUNCTION_NAME);
3674 dout_server<<"Server::peerAdded(): peer->id="
3675 <<peer->id<<std::endl;
3678 c.type = PEER_ADDED;
3679 c.peer_id = peer->id;
3681 m_peer_change_queue.push_back(c);
3684 void Server::deletingPeer(con::Peer *peer, bool timeout)
3686 DSTACK(__FUNCTION_NAME);
3687 dout_server<<"Server::deletingPeer(): peer->id="
3688 <<peer->id<<", timeout="<<timeout<<std::endl;
3691 c.type = PEER_REMOVED;
3692 c.peer_id = peer->id;
3693 c.timeout = timeout;
3694 m_peer_change_queue.push_back(c);
3701 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3703 DSTACK(__FUNCTION_NAME);
3704 std::ostringstream os(std::ios_base::binary);
3706 writeU16(os, TOCLIENT_HP);
3710 std::string s = os.str();
3711 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3713 con.Send(peer_id, 0, data, true);
3716 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3717 const std::wstring &reason)
3719 DSTACK(__FUNCTION_NAME);
3720 std::ostringstream os(std::ios_base::binary);
3722 writeU16(os, TOCLIENT_ACCESS_DENIED);
3723 os<<serializeWideString(reason);
3726 std::string s = os.str();
3727 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3729 con.Send(peer_id, 0, data, true);
3733 Non-static send methods
3736 void Server::SendObjectData(float dtime)
3738 DSTACK(__FUNCTION_NAME);
3740 core::map<v3s16, bool> stepped_blocks;
3742 for(core::map<u16, RemoteClient*>::Iterator
3743 i = m_clients.getIterator();
3744 i.atEnd() == false; i++)
3746 u16 peer_id = i.getNode()->getKey();
3747 RemoteClient *client = i.getNode()->getValue();
3748 assert(client->peer_id == peer_id);
3750 if(client->serialization_version == SER_FMT_VER_INVALID)
3753 client->SendObjectData(this, dtime, stepped_blocks);
3757 void Server::SendPlayerInfos()
3759 DSTACK(__FUNCTION_NAME);
3761 //JMutexAutoLock envlock(m_env_mutex);
3763 // Get connected players
3764 core::list<Player*> players = m_env.getPlayers(true);
3766 u32 player_count = players.getSize();
3767 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3769 SharedBuffer<u8> data(datasize);
3770 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3773 core::list<Player*>::Iterator i;
3774 for(i = players.begin();
3775 i != players.end(); i++)
3777 Player *player = *i;
3779 /*dstream<<"Server sending player info for player with "
3780 "peer_id="<<player->peer_id<<std::endl;*/
3782 writeU16(&data[start], player->peer_id);
3783 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3784 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3785 start += 2+PLAYERNAME_SIZE;
3788 //JMutexAutoLock conlock(m_con_mutex);
3791 m_con.SendToAll(0, data, true);
3794 void Server::SendInventory(u16 peer_id)
3796 DSTACK(__FUNCTION_NAME);
3798 Player* player = m_env.getPlayer(peer_id);
3805 std::ostringstream os;
3806 //os.imbue(std::locale("C"));
3808 player->inventory.serialize(os);
3810 std::string s = os.str();
3812 SharedBuffer<u8> data(s.size()+2);
3813 writeU16(&data[0], TOCLIENT_INVENTORY);
3814 memcpy(&data[2], s.c_str(), s.size());
3817 m_con.Send(peer_id, 0, data, true);
3820 std::string getWieldedItemString(const Player *player)
3822 const InventoryItem *item = player->getWieldItem();
3824 return std::string("");
3825 std::ostringstream os(std::ios_base::binary);
3826 item->serialize(os);
3830 void Server::SendWieldedItem(const Player* player)
3832 DSTACK(__FUNCTION_NAME);
3836 std::ostringstream os(std::ios_base::binary);
3838 writeU16(os, TOCLIENT_PLAYERITEM);
3840 writeU16(os, player->peer_id);
3841 os<<serializeString(getWieldedItemString(player));
3844 std::string s = os.str();
3845 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3847 m_con.SendToAll(0, data, true);
3850 void Server::SendPlayerItems()
3852 DSTACK(__FUNCTION_NAME);
3854 std::ostringstream os(std::ios_base::binary);
3855 core::list<Player *> players = m_env.getPlayers(true);
3857 writeU16(os, TOCLIENT_PLAYERITEM);
3858 writeU16(os, players.size());
3859 core::list<Player *>::Iterator i;
3860 for(i = players.begin(); i != players.end(); ++i)
3863 writeU16(os, p->peer_id);
3864 os<<serializeString(getWieldedItemString(p));
3868 std::string s = os.str();
3869 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3871 m_con.SendToAll(0, data, true);
3874 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3876 DSTACK(__FUNCTION_NAME);
3878 std::ostringstream os(std::ios_base::binary);
3882 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3883 os.write((char*)buf, 2);
3886 writeU16(buf, message.size());
3887 os.write((char*)buf, 2);
3890 for(u32 i=0; i<message.size(); i++)
3894 os.write((char*)buf, 2);
3898 std::string s = os.str();
3899 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3901 m_con.Send(peer_id, 0, data, true);
3904 void Server::BroadcastChatMessage(const std::wstring &message)
3906 for(core::map<u16, RemoteClient*>::Iterator
3907 i = m_clients.getIterator();
3908 i.atEnd() == false; i++)
3910 // Get client and check that it is valid
3911 RemoteClient *client = i.getNode()->getValue();
3912 assert(client->peer_id == i.getNode()->getKey());
3913 if(client->serialization_version == SER_FMT_VER_INVALID)
3916 SendChatMessage(client->peer_id, message);
3920 void Server::SendPlayerHP(Player *player)
3922 SendHP(m_con, player->peer_id, player->hp);
3925 void Server::SendMovePlayer(Player *player)
3927 DSTACK(__FUNCTION_NAME);
3928 std::ostringstream os(std::ios_base::binary);
3930 writeU16(os, TOCLIENT_MOVE_PLAYER);
3931 writeV3F1000(os, player->getPosition());
3932 writeF1000(os, player->getPitch());
3933 writeF1000(os, player->getYaw());
3936 v3f pos = player->getPosition();
3937 f32 pitch = player->getPitch();
3938 f32 yaw = player->getYaw();
3939 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3940 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3947 std::string s = os.str();
3948 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3950 m_con.Send(player->peer_id, 0, data, true);
3953 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3954 core::list<u16> *far_players, float far_d_nodes)
3956 float maxd = far_d_nodes*BS;
3957 v3f p_f = intToFloat(p, BS);
3961 SharedBuffer<u8> reply(replysize);
3962 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3963 writeS16(&reply[2], p.X);
3964 writeS16(&reply[4], p.Y);
3965 writeS16(&reply[6], p.Z);
3967 for(core::map<u16, RemoteClient*>::Iterator
3968 i = m_clients.getIterator();
3969 i.atEnd() == false; i++)
3971 // Get client and check that it is valid
3972 RemoteClient *client = i.getNode()->getValue();
3973 assert(client->peer_id == i.getNode()->getKey());
3974 if(client->serialization_version == SER_FMT_VER_INVALID)
3977 // Don't send if it's the same one
3978 if(client->peer_id == ignore_id)
3984 Player *player = m_env.getPlayer(client->peer_id);
3987 // If player is far away, only set modified blocks not sent
3988 v3f player_pos = player->getPosition();
3989 if(player_pos.getDistanceFrom(p_f) > maxd)
3991 far_players->push_back(client->peer_id);
3998 m_con.Send(client->peer_id, 0, reply, true);
4002 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4003 core::list<u16> *far_players, float far_d_nodes)
4005 float maxd = far_d_nodes*BS;
4006 v3f p_f = intToFloat(p, BS);
4008 for(core::map<u16, RemoteClient*>::Iterator
4009 i = m_clients.getIterator();
4010 i.atEnd() == false; i++)
4012 // Get client and check that it is valid
4013 RemoteClient *client = i.getNode()->getValue();
4014 assert(client->peer_id == i.getNode()->getKey());
4015 if(client->serialization_version == SER_FMT_VER_INVALID)
4018 // Don't send if it's the same one
4019 if(client->peer_id == ignore_id)
4025 Player *player = m_env.getPlayer(client->peer_id);
4028 // If player is far away, only set modified blocks not sent
4029 v3f player_pos = player->getPosition();
4030 if(player_pos.getDistanceFrom(p_f) > maxd)
4032 far_players->push_back(client->peer_id);
4039 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4040 SharedBuffer<u8> reply(replysize);
4041 writeU16(&reply[0], TOCLIENT_ADDNODE);
4042 writeS16(&reply[2], p.X);
4043 writeS16(&reply[4], p.Y);
4044 writeS16(&reply[6], p.Z);
4045 n.serialize(&reply[8], client->serialization_version);
4048 m_con.Send(client->peer_id, 0, reply, true);
4052 void Server::setBlockNotSent(v3s16 p)
4054 for(core::map<u16, RemoteClient*>::Iterator
4055 i = m_clients.getIterator();
4056 i.atEnd()==false; i++)
4058 RemoteClient *client = i.getNode()->getValue();
4059 client->SetBlockNotSent(p);
4063 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4065 DSTACK(__FUNCTION_NAME);
4067 v3s16 p = block->getPos();
4071 bool completely_air = true;
4072 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4073 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4074 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4076 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4078 completely_air = false;
4079 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4084 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4086 dstream<<"[completely air] ";
4091 Create a packet with the block in the right format
4094 std::ostringstream os(std::ios_base::binary);
4095 block->serialize(os, ver);
4096 std::string s = os.str();
4097 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4099 u32 replysize = 8 + blockdata.getSize();
4100 SharedBuffer<u8> reply(replysize);
4101 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4102 writeS16(&reply[2], p.X);
4103 writeS16(&reply[4], p.Y);
4104 writeS16(&reply[6], p.Z);
4105 memcpy(&reply[8], *blockdata, blockdata.getSize());
4107 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4108 <<": \tpacket size: "<<replysize<<std::endl;*/
4113 m_con.Send(peer_id, 1, reply, true);
4116 void Server::SendBlocks(float dtime)
4118 DSTACK(__FUNCTION_NAME);
4120 JMutexAutoLock envlock(m_env_mutex);
4121 JMutexAutoLock conlock(m_con_mutex);
4123 //TimeTaker timer("Server::SendBlocks");
4125 core::array<PrioritySortedBlockTransfer> queue;
4127 s32 total_sending = 0;
4130 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4132 for(core::map<u16, RemoteClient*>::Iterator
4133 i = m_clients.getIterator();
4134 i.atEnd() == false; i++)
4136 RemoteClient *client = i.getNode()->getValue();
4137 assert(client->peer_id == i.getNode()->getKey());
4139 total_sending += client->SendingCount();
4141 if(client->serialization_version == SER_FMT_VER_INVALID)
4144 client->GetNextBlocks(this, dtime, queue);
4149 // Lowest priority number comes first.
4150 // Lowest is most important.
4153 for(u32 i=0; i<queue.size(); i++)
4155 //TODO: Calculate limit dynamically
4156 if(total_sending >= g_settings->getS32
4157 ("max_simultaneous_block_sends_server_total"))
4160 PrioritySortedBlockTransfer q = queue[i];
4162 MapBlock *block = NULL;
4165 block = m_env.getMap().getBlockNoCreate(q.pos);
4167 catch(InvalidPositionException &e)
4172 RemoteClient *client = getClient(q.peer_id);
4174 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4176 client->SentBlock(q.pos);
4186 void Server::UpdateCrafting(u16 peer_id)
4188 DSTACK(__FUNCTION_NAME);
4190 Player* player = m_env.getPlayer(peer_id);
4194 Calculate crafting stuff
4196 if(g_settings->getBool("creative_mode") == false)
4198 InventoryList *clist = player->inventory.getList("craft");
4199 InventoryList *rlist = player->inventory.getList("craftresult");
4201 if(rlist && rlist->getUsedSlots() == 0)
4202 player->craftresult_is_preview = true;
4204 if(rlist && player->craftresult_is_preview)
4206 rlist->clearItems();
4208 if(clist && rlist && player->craftresult_is_preview)
4210 InventoryItem *items[9];
4211 for(u16 i=0; i<9; i++)
4213 items[i] = clist->getItem(i);
4216 // Get result of crafting grid
4217 InventoryItem *result = craft_get_result(items);
4219 rlist->addItem(result);
4222 } // if creative_mode == false
4225 RemoteClient* Server::getClient(u16 peer_id)
4227 DSTACK(__FUNCTION_NAME);
4228 //JMutexAutoLock lock(m_con_mutex);
4229 core::map<u16, RemoteClient*>::Node *n;
4230 n = m_clients.find(peer_id);
4231 // A client should exist for all peers
4233 return n->getValue();
4236 std::wstring Server::getStatusString()
4238 std::wostringstream os(std::ios_base::binary);
4241 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4243 os<<L", uptime="<<m_uptime.get();
4244 // Information about clients
4246 for(core::map<u16, RemoteClient*>::Iterator
4247 i = m_clients.getIterator();
4248 i.atEnd() == false; i++)
4250 // Get client and check that it is valid
4251 RemoteClient *client = i.getNode()->getValue();
4252 assert(client->peer_id == i.getNode()->getKey());
4253 if(client->serialization_version == SER_FMT_VER_INVALID)
4256 Player *player = m_env.getPlayer(client->peer_id);
4257 // Get name of player
4258 std::wstring name = L"unknown";
4260 name = narrow_to_wide(player->getName());
4261 // Add name to information string
4265 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4266 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4267 if(g_settings->get("motd") != "")
4268 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4272 // Saves g_settings to configpath given at initialization
4273 void Server::saveConfig()
4275 if(m_configpath != "")
4276 g_settings->updateConfigFile(m_configpath.c_str());
4279 v3f findSpawnPos(ServerMap &map)
4281 //return v3f(50,50,50)*BS;
4284 s16 groundheight = 0;
4287 nodepos = v2s16(0,0);
4292 // Try to find a good place a few times
4293 for(s32 i=0; i<1000; i++)
4296 // We're going to try to throw the player to this position
4297 nodepos = v2s16(-range + (myrand()%(range*2)),
4298 -range + (myrand()%(range*2)));
4299 v2s16 sectorpos = getNodeSectorPos(nodepos);
4300 // Get sector (NOTE: Don't get because it's slow)
4301 //m_env.getMap().emergeSector(sectorpos);
4302 // Get ground height at point (fallbacks to heightmap function)
4303 groundheight = map.findGroundLevel(nodepos);
4304 // Don't go underwater
4305 if(groundheight < WATER_LEVEL)
4307 //dstream<<"-> Underwater"<<std::endl;
4310 // Don't go to high places
4311 if(groundheight > WATER_LEVEL + 4)
4313 //dstream<<"-> Underwater"<<std::endl;
4317 // Found a good place
4318 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4323 // If no suitable place was not found, go above water at least.
4324 if(groundheight < WATER_LEVEL)
4325 groundheight = WATER_LEVEL;
4327 return intToFloat(v3s16(
4334 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4337 Try to get an existing player
4339 Player *player = m_env.getPlayer(name);
4342 // If player is already connected, cancel
4343 if(player->peer_id != 0)
4345 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4350 player->peer_id = peer_id;
4352 // Reset inventory to creative if in creative mode
4353 if(g_settings->getBool("creative_mode"))
4355 // Warning: double code below
4356 // Backup actual inventory
4357 player->inventory_backup = new Inventory();
4358 *(player->inventory_backup) = player->inventory;
4359 // Set creative inventory
4360 craft_set_creative_inventory(player);
4367 If player with the wanted peer_id already exists, cancel.
4369 if(m_env.getPlayer(peer_id) != NULL)
4371 dstream<<"emergePlayer(): Player with wrong name but same"
4372 " peer_id already exists"<<std::endl;
4380 player = new ServerRemotePlayer();
4381 //player->peer_id = c.peer_id;
4382 //player->peer_id = PEER_ID_INEXISTENT;
4383 player->peer_id = peer_id;
4384 player->updateName(name);
4385 m_authmanager.add(name);
4386 m_authmanager.setPassword(name, password);
4387 m_authmanager.setPrivs(name,
4388 stringToPrivs(g_settings->get("default_privs")));
4394 dstream<<"Server: Finding spawn place for player \""
4395 <<player->getName()<<"\""<<std::endl;
4397 v3f pos = findSpawnPos(m_env.getServerMap());
4399 player->setPosition(pos);
4402 Add player to environment
4405 m_env.addPlayer(player);
4408 Add stuff to inventory
4411 if(g_settings->getBool("creative_mode"))
4413 // Warning: double code above
4414 // Backup actual inventory
4415 player->inventory_backup = new Inventory();
4416 *(player->inventory_backup) = player->inventory;
4417 // Set creative inventory
4418 craft_set_creative_inventory(player);
4420 else if(g_settings->getBool("give_initial_stuff"))
4422 craft_give_initial_stuff(player);
4427 } // create new player
4430 void Server::handlePeerChange(PeerChange &c)
4432 JMutexAutoLock envlock(m_env_mutex);
4433 JMutexAutoLock conlock(m_con_mutex);
4435 if(c.type == PEER_ADDED)
4442 core::map<u16, RemoteClient*>::Node *n;
4443 n = m_clients.find(c.peer_id);
4444 // The client shouldn't already exist
4448 RemoteClient *client = new RemoteClient();
4449 client->peer_id = c.peer_id;
4450 m_clients.insert(client->peer_id, client);
4453 else if(c.type == PEER_REMOVED)
4460 core::map<u16, RemoteClient*>::Node *n;
4461 n = m_clients.find(c.peer_id);
4462 // The client should exist
4466 Mark objects to be not known by the client
4468 RemoteClient *client = n->getValue();
4470 for(core::map<u16, bool>::Iterator
4471 i = client->m_known_objects.getIterator();
4472 i.atEnd()==false; i++)
4475 u16 id = i.getNode()->getKey();
4476 ServerActiveObject* obj = m_env.getActiveObject(id);
4478 if(obj && obj->m_known_by_count > 0)
4479 obj->m_known_by_count--;
4482 // Collect information about leaving in chat
4483 std::wstring message;
4485 Player *player = m_env.getPlayer(c.peer_id);
4488 std::wstring name = narrow_to_wide(player->getName());
4491 message += L" left game";
4493 message += L" (timed out)";
4499 m_env.removePlayer(c.peer_id);
4502 // Set player client disconnected
4504 Player *player = m_env.getPlayer(c.peer_id);
4506 player->peer_id = 0;
4510 delete m_clients[c.peer_id];
4511 m_clients.remove(c.peer_id);
4513 // Send player info to all remaining clients
4516 // Send leave chat message to all remaining clients
4517 BroadcastChatMessage(message);
4526 void Server::handlePeerChanges()
4528 while(m_peer_change_queue.size() > 0)
4530 PeerChange c = m_peer_change_queue.pop_front();
4532 dout_server<<"Server: Handling peer change: "
4533 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4536 handlePeerChange(c);
4540 u64 Server::getPlayerPrivs(Player *player)
4544 std::string playername = player->getName();
4545 // Local player gets all privileges regardless of
4546 // what's set on their account.
4547 if(g_settings->get("name") == playername)
4553 return getPlayerAuthPrivs(playername);
4557 void dedicated_server_loop(Server &server, bool &kill)
4559 DSTACK(__FUNCTION_NAME);
4561 dstream<<DTIME<<std::endl;
4562 dstream<<"========================"<<std::endl;
4563 dstream<<"Running dedicated server"<<std::endl;
4564 dstream<<"========================"<<std::endl;
4567 IntervalLimiter m_profiler_interval;
4571 // This is kind of a hack but can be done like this
4572 // because server.step() is very light
4574 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4579 if(server.getShutdownRequested() || kill)
4581 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4588 float profiler_print_interval =
4589 g_settings->getFloat("profiler_print_interval");
4590 if(profiler_print_interval != 0)
4592 if(m_profiler_interval.step(0.030, profiler_print_interval))
4594 dstream<<"Profiler:"<<std::endl;
4595 g_profiler->print(dstream);
4596 g_profiler->clear();
4603 static int counter = 0;
4609 core::list<PlayerInfo> list = server.getPlayerInfo();
4610 core::list<PlayerInfo>::Iterator i;
4611 static u32 sum_old = 0;
4612 u32 sum = PIChecksum(list);
4615 dstream<<DTIME<<"Player info:"<<std::endl;
4616 for(i=list.begin(); i!=list.end(); i++)
4618 i->PrintLine(&dstream);