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"
40 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
42 class MapEditEventIgnorer
45 MapEditEventIgnorer(bool *flag):
54 ~MapEditEventIgnorer()
67 void * ServerThread::Thread()
71 DSTACK(__FUNCTION_NAME);
73 BEGIN_DEBUG_EXCEPTION_HANDLER
78 //TimeTaker timer("AsyncRunStep() + Receive()");
81 //TimeTaker timer("AsyncRunStep()");
82 m_server->AsyncRunStep();
85 //dout_server<<"Running m_server->Receive()"<<std::endl;
88 catch(con::NoIncomingDataException &e)
91 catch(con::PeerNotFoundException &e)
93 dout_server<<"Server: PeerNotFoundException"<<std::endl;
97 END_DEBUG_EXCEPTION_HANDLER
102 void * EmergeThread::Thread()
106 DSTACK(__FUNCTION_NAME);
108 BEGIN_DEBUG_EXCEPTION_HANDLER
110 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
113 Get block info from queue, emerge them and send them
116 After queue is empty, exit.
120 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
124 SharedPtr<QueuedBlockEmerge> q(qptr);
130 Do not generate over-limit
132 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
140 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
142 //TimeTaker timer("block emerge");
145 Try to emerge it from somewhere.
147 If it is only wanted as optional, only loading from disk
152 Check if any peer wants it as non-optional. In that case it
155 Also decrement the emerge queue count in clients.
158 bool only_from_disk = true;
161 core::map<u16, u8>::Iterator i;
162 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
164 //u16 peer_id = i.getNode()->getKey();
167 u8 flags = i.getNode()->getValue();
168 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
169 only_from_disk = false;
174 if(enable_mapgen_debug_info)
175 dstream<<"EmergeThread: p="
176 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
177 <<"only_from_disk="<<only_from_disk<<std::endl;
179 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
181 //core::map<v3s16, MapBlock*> changed_blocks;
182 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
184 MapBlock *block = NULL;
185 bool got_block = true;
186 core::map<v3s16, MapBlock*> modified_blocks;
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 //map.loadSectorFull(p2d);
197 map.loadSectorMeta(p2d);
199 block = map.getBlockNoCreateNoEx(p);
200 if(!block || block->isDummy() || !block->isGenerated())
202 if(enable_mapgen_debug_info)
203 dstream<<"EmergeThread: not in memory, loading"<<std::endl;
205 // Get, load or create sector
206 /*ServerMapSector *sector =
207 (ServerMapSector*)map.createSector(p2d);*/
209 // Load/generate block
211 /*block = map.emergeBlock(p, sector, changed_blocks,
212 lighting_invalidated_blocks);*/
214 block = map.loadBlock(p);
216 if(only_from_disk == false)
218 if(block == NULL || block->isGenerated() == false)
220 if(enable_mapgen_debug_info)
221 dstream<<"EmergeThread: generating"<<std::endl;
222 block = map.generateBlock(p, modified_blocks);
226 if(enable_mapgen_debug_info)
227 dstream<<"EmergeThread: ended up with: "
228 <<analyze_block(block)<<std::endl;
237 Ignore map edit events, they will not need to be
238 sent to anybody because the block hasn't been sent
241 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
243 // Activate objects and stuff
244 m_server->m_env.activateBlock(block, 3600);
249 /*if(block->getLightingExpired()){
250 lighting_invalidated_blocks[block->getPos()] = block;
254 // TODO: Some additional checking and lighting updating,
259 JMutexAutoLock envlock(m_server->m_env_mutex);
264 Collect a list of blocks that have been modified in
265 addition to the fetched one.
269 if(lighting_invalidated_blocks.size() > 0)
271 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
272 <<" blocks"<<std::endl;*/
274 // 50-100ms for single block generation
275 //TimeTaker timer("** EmergeThread updateLighting");
277 // Update lighting without locking the environment mutex,
278 // add modified blocks to changed blocks
279 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
282 // Add all from changed_blocks to modified_blocks
283 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
284 i.atEnd() == false; i++)
286 MapBlock *block = i.getNode()->getValue();
287 modified_blocks.insert(block->getPos(), block);
291 // If we got no block, there should be no invalidated blocks
294 //assert(lighting_invalidated_blocks.size() == 0);
300 Set sent status of modified blocks on clients
303 // NOTE: Server's clients are also behind the connection mutex
304 JMutexAutoLock lock(m_server->m_con_mutex);
307 Add the originally fetched block to the modified list
311 modified_blocks.insert(p, block);
315 Set the modified blocks unsent for all the clients
318 for(core::map<u16, RemoteClient*>::Iterator
319 i = m_server->m_clients.getIterator();
320 i.atEnd() == false; i++)
322 RemoteClient *client = i.getNode()->getValue();
324 if(modified_blocks.size() > 0)
326 // Remove block from sent history
327 client->SetBlocksNotSent(modified_blocks);
333 END_DEBUG_EXCEPTION_HANDLER
338 void RemoteClient::GetNextBlocks(Server *server, float dtime,
339 core::array<PrioritySortedBlockTransfer> &dest)
341 DSTACK(__FUNCTION_NAME);
344 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
347 m_nothing_to_send_pause_timer -= dtime;
349 if(m_nothing_to_send_pause_timer >= 0)
352 m_nearest_unsent_reset_timer = 0;
356 // Won't send anything if already sending
357 if(m_blocks_sending.size() >= g_settings.getU16
358 ("max_simultaneous_block_sends_per_client"))
360 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
364 //TimeTaker timer("RemoteClient::GetNextBlocks");
366 Player *player = server->m_env.getPlayer(peer_id);
368 assert(player != NULL);
370 v3f playerpos = player->getPosition();
371 v3f playerspeed = player->getSpeed();
372 v3f playerspeeddir(0,0,0);
373 if(playerspeed.getLength() > 1.0*BS)
374 playerspeeddir = playerspeed / playerspeed.getLength();
375 // Predict to next block
376 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
378 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
380 v3s16 center = getNodeBlockPos(center_nodepos);
382 // Camera position and direction
383 v3f camera_pos = player->getEyePosition();
384 v3f camera_dir = v3f(0,0,1);
385 camera_dir.rotateYZBy(player->getPitch());
386 camera_dir.rotateXZBy(player->getYaw());
388 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
389 <<camera_dir.Z<<")"<<std::endl;*/
392 Get the starting value of the block finder radius.
395 if(m_last_center != center)
397 m_nearest_unsent_d = 0;
398 m_last_center = center;
401 /*dstream<<"m_nearest_unsent_reset_timer="
402 <<m_nearest_unsent_reset_timer<<std::endl;*/
404 // This has to be incremented only when the nothing to send pause
406 m_nearest_unsent_reset_timer += dtime;
408 // Reset periodically to avoid possible bugs or other mishaps
409 if(m_nearest_unsent_reset_timer > 10.0)
411 m_nearest_unsent_reset_timer = 0;
412 m_nearest_unsent_d = 0;
413 /*dstream<<"Resetting m_nearest_unsent_d for "
414 <<server->getPlayerName(peer_id)<<std::endl;*/
417 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
418 s16 d_start = m_nearest_unsent_d;
420 //dstream<<"d_start="<<d_start<<std::endl;
422 u16 max_simul_sends_setting = g_settings.getU16
423 ("max_simultaneous_block_sends_per_client");
424 u16 max_simul_sends_usually = max_simul_sends_setting;
427 Check the time from last addNode/removeNode.
429 Decrease send rate if player is building stuff.
431 m_time_from_building += dtime;
432 if(m_time_from_building < g_settings.getFloat(
433 "full_block_send_enable_min_time_from_building"))
435 max_simul_sends_usually
436 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
440 Number of blocks sending + number of blocks selected for sending
442 u32 num_blocks_selected = m_blocks_sending.size();
445 next time d will be continued from the d from which the nearest
446 unsent block was found this time.
448 This is because not necessarily any of the blocks found this
449 time are actually sent.
451 s32 new_nearest_unsent_d = -1;
453 s16 d_max = g_settings.getS16("max_block_send_distance");
454 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
456 // Don't loop very much at a time
457 if(d_max > d_start+1)
459 /*if(d_max_gen > d_start+2)
460 d_max_gen = d_start+2;*/
462 //dstream<<"Starting from "<<d_start<<std::endl;
464 bool sending_something = false;
466 bool no_blocks_found_for_sending = true;
468 bool queue_is_full = false;
471 for(d = d_start; d <= d_max; d++)
473 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
476 If m_nearest_unsent_d was changed by the EmergeThread
477 (it can change it to 0 through SetBlockNotSent),
479 Else update m_nearest_unsent_d
481 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
483 d = m_nearest_unsent_d;
484 last_nearest_unsent_d = m_nearest_unsent_d;
488 Get the border/face dot coordinates of a "d-radiused"
491 core::list<v3s16> list;
492 getFacePositions(list, d);
494 core::list<v3s16>::Iterator li;
495 for(li=list.begin(); li!=list.end(); li++)
497 v3s16 p = *li + center;
501 - Don't allow too many simultaneous transfers
502 - EXCEPT when the blocks are very close
504 Also, don't send blocks that are already flying.
507 // Start with the usual maximum
508 u16 max_simul_dynamic = max_simul_sends_usually;
510 // If block is very close, allow full maximum
511 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
512 max_simul_dynamic = max_simul_sends_setting;
514 // Don't select too many blocks for sending
515 if(num_blocks_selected >= max_simul_dynamic)
517 queue_is_full = true;
518 goto queue_full_break;
521 // Don't send blocks that are currently being transferred
522 if(m_blocks_sending.find(p) != NULL)
528 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
529 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
536 // If this is true, inexistent block will be made from scratch
537 bool generate = d <= d_max_gen;
540 /*// Limit the generating area vertically to 2/3
541 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
544 // Limit the send area vertically to 2/3
545 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
551 If block is far away, don't generate it unless it is
557 // Block center y in nodes
558 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
559 // Don't generate if it's very high or very low
560 if(y < -64 || y > 64)
564 v2s16 p2d_nodes_center(
568 // Get ground height in nodes
569 s16 gh = server->m_env.getServerMap().findGroundLevel(
572 // If differs a lot, don't generate
573 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
575 // Actually, don't even send it
581 //dstream<<"d="<<d<<std::endl;
584 Don't generate or send if not in sight
585 FIXME This only works if the client uses a small enough
586 FOV setting. The default of 72 degrees is fine.
589 float camera_fov = (72.0*PI/180) * 4./3.;
590 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
596 Don't send already sent blocks
599 if(m_blocks_sent.find(p) != NULL)
606 Check if map has this block
608 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
610 bool surely_not_found_on_disk = false;
611 bool block_is_invalid = false;
614 // Reset usage timer, this block will be of use in the future.
615 block->resetUsageTimer();
617 // Block is dummy if data doesn't exist.
618 // It means it has been not found from disk and not generated
621 surely_not_found_on_disk = true;
624 // Block is valid if lighting is up-to-date and data exists
625 if(block->isValid() == false)
627 block_is_invalid = true;
630 /*if(block->isFullyGenerated() == false)
632 block_is_invalid = true;
637 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
638 v2s16 chunkpos = map->sector_to_chunk(p2d);
639 if(map->chunkNonVolatile(chunkpos) == false)
640 block_is_invalid = true;
642 if(block->isGenerated() == false)
643 block_is_invalid = true;
646 If block is not close, don't send it unless it is near
649 Block is near ground level if night-time mesh
650 differs from day-time mesh.
654 if(block->dayNightDiffed() == false)
661 If block has been marked to not exist on disk (dummy)
662 and generating new ones is not wanted, skip block.
664 if(generate == false && surely_not_found_on_disk == true)
671 Record the lowest d from which a block has been
672 found being not sent and possibly to exist
674 if(no_blocks_found_for_sending)
677 new_nearest_unsent_d = d;
680 no_blocks_found_for_sending = false;
683 Add inexistent block to emerge queue.
685 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
687 //TODO: Get value from somewhere
688 // Allow only one block in emerge queue
689 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
690 // Allow two blocks in queue per client
691 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
693 //dstream<<"Adding block to emerge queue"<<std::endl;
695 // Add it to the emerge queue and trigger the thread
698 if(generate == false)
699 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
701 server->m_emerge_queue.addBlock(peer_id, p, flags);
702 server->m_emergethread.trigger();
710 Add block to send queue
713 PrioritySortedBlockTransfer q((float)d, p, peer_id);
717 num_blocks_selected += 1;
718 sending_something = true;
723 //dstream<<"Stopped at "<<d<<std::endl;
725 if(no_blocks_found_for_sending)
727 if(queue_is_full == false)
728 new_nearest_unsent_d = d;
731 if(new_nearest_unsent_d != -1)
732 m_nearest_unsent_d = new_nearest_unsent_d;
734 if(sending_something == false)
736 m_nothing_to_send_counter++;
737 if((s16)m_nothing_to_send_counter >=
738 g_settings.getS16("max_block_send_distance"))
740 // Pause time in seconds
741 m_nothing_to_send_pause_timer = 1.0;
742 /*dstream<<"nothing to send to "
743 <<server->getPlayerName(peer_id)
744 <<" (d="<<d<<")"<<std::endl;*/
749 m_nothing_to_send_counter = 0;
752 /*timer_result = timer.stop(true);
753 if(timer_result != 0)
754 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
757 void RemoteClient::SendObjectData(
760 core::map<v3s16, bool> &stepped_blocks
763 DSTACK(__FUNCTION_NAME);
765 // Can't send anything without knowing version
766 if(serialization_version == SER_FMT_VER_INVALID)
768 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
774 Send a TOCLIENT_OBJECTDATA packet.
778 u16 number of player positions
790 std::ostringstream os(std::ios_base::binary);
794 writeU16(buf, TOCLIENT_OBJECTDATA);
795 os.write((char*)buf, 2);
798 Get and write player data
801 // Get connected players
802 core::list<Player*> players = server->m_env.getPlayers(true);
804 // Write player count
805 u16 playercount = players.size();
806 writeU16(buf, playercount);
807 os.write((char*)buf, 2);
809 core::list<Player*>::Iterator i;
810 for(i = players.begin();
811 i != players.end(); i++)
815 v3f pf = player->getPosition();
816 v3f sf = player->getSpeed();
818 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
819 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
820 s32 pitch_i (player->getPitch() * 100);
821 s32 yaw_i (player->getYaw() * 100);
823 writeU16(buf, player->peer_id);
824 os.write((char*)buf, 2);
825 writeV3S32(buf, position_i);
826 os.write((char*)buf, 12);
827 writeV3S32(buf, speed_i);
828 os.write((char*)buf, 12);
829 writeS32(buf, pitch_i);
830 os.write((char*)buf, 4);
831 writeS32(buf, yaw_i);
832 os.write((char*)buf, 4);
836 Get and write object data
842 For making players to be able to build to their nearby
843 environment (building is not possible on blocks that are not
846 - Add blocks to emerge queue if they are not found
848 SUGGESTION: These could be ignored from the backside of the player
851 Player *player = server->m_env.getPlayer(peer_id);
855 v3f playerpos = player->getPosition();
856 v3f playerspeed = player->getSpeed();
858 v3s16 center_nodepos = floatToInt(playerpos, BS);
859 v3s16 center = getNodeBlockPos(center_nodepos);
861 s16 d_max = g_settings.getS16("active_object_range");
863 // Number of blocks whose objects were written to bos
866 std::ostringstream bos(std::ios_base::binary);
868 for(s16 d = 0; d <= d_max; d++)
870 core::list<v3s16> list;
871 getFacePositions(list, d);
873 core::list<v3s16>::Iterator li;
874 for(li=list.begin(); li!=list.end(); li++)
876 v3s16 p = *li + center;
879 Ignore blocks that haven't been sent to the client
882 if(m_blocks_sent.find(p) == NULL)
886 // Try stepping block and add it to a send queue
891 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
894 Step block if not in stepped_blocks and add to stepped_blocks.
896 if(stepped_blocks.find(p) == NULL)
898 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
899 stepped_blocks.insert(p, true);
900 block->setChangedFlag();
903 // Skip block if there are no objects
904 if(block->getObjectCount() == 0)
913 bos.write((char*)buf, 6);
916 //block->serializeObjects(bos, serialization_version); // DEPRECATED
923 Stop collecting objects if data is already too big
925 // Sum of player and object data sizes
926 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
927 // break out if data too big
928 if(sum > MAX_OBJECTDATA_SIZE)
930 goto skip_subsequent;
934 catch(InvalidPositionException &e)
937 // Add it to the emerge queue and trigger the thread.
938 // Fetch the block only if it is on disk.
940 // Grab and increment counter
941 /*SharedPtr<JMutexAutoLock> lock
942 (m_num_blocks_in_emerge_queue.getLock());
943 m_num_blocks_in_emerge_queue.m_value++;*/
945 // Add to queue as an anonymous fetch from disk
946 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
947 server->m_emerge_queue.addBlock(0, p, flags);
948 server->m_emergethread.trigger();
956 writeU16(buf, blockcount);
957 os.write((char*)buf, 2);
959 // Write block objects
966 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
969 std::string s = os.str();
970 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
971 // Send as unreliable
972 server->m_con.Send(peer_id, 0, data, false);
975 void RemoteClient::GotBlock(v3s16 p)
977 if(m_blocks_sending.find(p) != NULL)
978 m_blocks_sending.remove(p);
981 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
982 " m_blocks_sending"<<std::endl;*/
983 m_excess_gotblocks++;
985 m_blocks_sent.insert(p, true);
988 void RemoteClient::SentBlock(v3s16 p)
990 if(m_blocks_sending.find(p) == NULL)
991 m_blocks_sending.insert(p, 0.0);
993 dstream<<"RemoteClient::SentBlock(): Sent block"
994 " already in m_blocks_sending"<<std::endl;
997 void RemoteClient::SetBlockNotSent(v3s16 p)
999 m_nearest_unsent_d = 0;
1001 if(m_blocks_sending.find(p) != NULL)
1002 m_blocks_sending.remove(p);
1003 if(m_blocks_sent.find(p) != NULL)
1004 m_blocks_sent.remove(p);
1007 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
1009 m_nearest_unsent_d = 0;
1011 for(core::map<v3s16, MapBlock*>::Iterator
1012 i = blocks.getIterator();
1013 i.atEnd()==false; i++)
1015 v3s16 p = i.getNode()->getKey();
1017 if(m_blocks_sending.find(p) != NULL)
1018 m_blocks_sending.remove(p);
1019 if(m_blocks_sent.find(p) != NULL)
1020 m_blocks_sent.remove(p);
1028 PlayerInfo::PlayerInfo()
1034 void PlayerInfo::PrintLine(std::ostream *s)
1037 (*s)<<"\""<<name<<"\" ("
1038 <<(position.X/10)<<","<<(position.Y/10)
1039 <<","<<(position.Z/10)<<") ";
1041 (*s)<<" avg_rtt="<<avg_rtt;
1045 u32 PIChecksum(core::list<PlayerInfo> &l)
1047 core::list<PlayerInfo>::Iterator i;
1050 for(i=l.begin(); i!=l.end(); i++)
1052 checksum += a * (i->id+1);
1053 checksum ^= 0x435aafcd;
1064 std::string mapsavedir,
1065 std::string configpath
1067 m_env(new ServerMap(mapsavedir), this),
1068 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1069 m_authmanager(mapsavedir+"/auth.txt"),
1070 m_banmanager(mapsavedir+"/ipban.txt"),
1072 m_emergethread(this),
1074 m_time_of_day_send_timer(0),
1076 m_mapsavedir(mapsavedir),
1077 m_configpath(configpath),
1078 m_shutdown_requested(false),
1079 m_ignore_map_edit_events(false),
1080 m_ignore_map_edit_events_peer_id(0)
1082 m_liquid_transform_timer = 0.0;
1083 m_print_info_timer = 0.0;
1084 m_objectdata_timer = 0.0;
1085 m_emergethread_trigger_timer = 0.0;
1086 m_savemap_timer = 0.0;
1090 m_step_dtime_mutex.Init();
1093 // Register us to receive map edit events
1094 m_env.getMap().addEventReceiver(this);
1096 // If file exists, load environment metadata
1097 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1099 dstream<<"Server: Loading environment metadata"<<std::endl;
1100 m_env.loadMeta(m_mapsavedir);
1104 dstream<<"Server: Loading players"<<std::endl;
1105 m_env.deSerializePlayers(m_mapsavedir);
1110 dstream<<"Server::~Server()"<<std::endl;
1113 Send shutdown message
1116 JMutexAutoLock conlock(m_con_mutex);
1118 std::wstring line = L"*** Server shutting down";
1121 Send the message to clients
1123 for(core::map<u16, RemoteClient*>::Iterator
1124 i = m_clients.getIterator();
1125 i.atEnd() == false; i++)
1127 // Get client and check that it is valid
1128 RemoteClient *client = i.getNode()->getValue();
1129 assert(client->peer_id == i.getNode()->getKey());
1130 if(client->serialization_version == SER_FMT_VER_INVALID)
1134 SendChatMessage(client->peer_id, line);
1136 catch(con::PeerNotFoundException &e)
1144 dstream<<"Server: Saving players"<<std::endl;
1145 m_env.serializePlayers(m_mapsavedir);
1148 Save environment metadata
1150 dstream<<"Server: Saving environment metadata"<<std::endl;
1151 m_env.saveMeta(m_mapsavedir);
1162 JMutexAutoLock clientslock(m_con_mutex);
1164 for(core::map<u16, RemoteClient*>::Iterator
1165 i = m_clients.getIterator();
1166 i.atEnd() == false; i++)
1169 // NOTE: These are removed by env destructor
1171 u16 peer_id = i.getNode()->getKey();
1172 JMutexAutoLock envlock(m_env_mutex);
1173 m_env.removePlayer(peer_id);
1177 delete i.getNode()->getValue();
1182 void Server::start(unsigned short port)
1184 DSTACK(__FUNCTION_NAME);
1185 // Stop thread if already running
1188 // Initialize connection
1189 m_con.setTimeoutMs(30);
1193 m_thread.setRun(true);
1196 dout_server<<"Server: Started on port "<<port<<std::endl;
1201 DSTACK(__FUNCTION_NAME);
1203 // Stop threads (set run=false first so both start stopping)
1204 m_thread.setRun(false);
1205 m_emergethread.setRun(false);
1207 m_emergethread.stop();
1209 dout_server<<"Server: Threads stopped"<<std::endl;
1212 void Server::step(float dtime)
1214 DSTACK(__FUNCTION_NAME);
1219 JMutexAutoLock lock(m_step_dtime_mutex);
1220 m_step_dtime += dtime;
1224 void Server::AsyncRunStep()
1226 DSTACK(__FUNCTION_NAME);
1230 JMutexAutoLock lock1(m_step_dtime_mutex);
1231 dtime = m_step_dtime;
1235 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1236 "blocks to clients");
1237 // Send blocks to clients
1244 //dstream<<"Server steps "<<dtime<<std::endl;
1245 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1248 JMutexAutoLock lock1(m_step_dtime_mutex);
1249 m_step_dtime -= dtime;
1256 m_uptime.set(m_uptime.get() + dtime);
1260 // Process connection's timeouts
1261 JMutexAutoLock lock2(m_con_mutex);
1262 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1263 m_con.RunTimeouts(dtime);
1267 // This has to be called so that the client list gets synced
1268 // with the peer list of the connection
1269 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1270 handlePeerChanges();
1274 Update m_time_of_day and overall game time
1277 JMutexAutoLock envlock(m_env_mutex);
1279 m_time_counter += dtime;
1280 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1281 u32 units = (u32)(m_time_counter*speed);
1282 m_time_counter -= (f32)units / speed;
1284 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1286 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1289 Send to clients at constant intervals
1292 m_time_of_day_send_timer -= dtime;
1293 if(m_time_of_day_send_timer < 0.0)
1295 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1297 //JMutexAutoLock envlock(m_env_mutex);
1298 JMutexAutoLock conlock(m_con_mutex);
1300 for(core::map<u16, RemoteClient*>::Iterator
1301 i = m_clients.getIterator();
1302 i.atEnd() == false; i++)
1304 RemoteClient *client = i.getNode()->getValue();
1305 //Player *player = m_env.getPlayer(client->peer_id);
1307 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1308 m_env.getTimeOfDay());
1310 m_con.Send(client->peer_id, 0, data, true);
1316 JMutexAutoLock lock(m_env_mutex);
1318 ScopeProfiler sp(&g_profiler, "Server: environment step");
1322 const float map_timer_and_unload_dtime = 5.15;
1323 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1325 JMutexAutoLock lock(m_env_mutex);
1326 // Run Map's timers and unload unused data
1327 ScopeProfiler sp(&g_profiler, "Server: map timer and unload");
1328 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1329 g_settings.getFloat("server_unload_unused_data_timeout"));
1339 m_liquid_transform_timer += dtime;
1340 if(m_liquid_transform_timer >= 1.00)
1342 m_liquid_transform_timer -= 1.00;
1344 JMutexAutoLock lock(m_env_mutex);
1346 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1348 core::map<v3s16, MapBlock*> modified_blocks;
1349 m_env.getMap().transformLiquids(modified_blocks);
1354 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1355 ServerMap &map = ((ServerMap&)m_env.getMap());
1356 map.updateLighting(modified_blocks, lighting_modified_blocks);
1358 // Add blocks modified by lighting to modified_blocks
1359 for(core::map<v3s16, MapBlock*>::Iterator
1360 i = lighting_modified_blocks.getIterator();
1361 i.atEnd() == false; i++)
1363 MapBlock *block = i.getNode()->getValue();
1364 modified_blocks.insert(block->getPos(), block);
1368 Set the modified blocks unsent for all the clients
1371 JMutexAutoLock lock2(m_con_mutex);
1373 for(core::map<u16, RemoteClient*>::Iterator
1374 i = m_clients.getIterator();
1375 i.atEnd() == false; i++)
1377 RemoteClient *client = i.getNode()->getValue();
1379 if(modified_blocks.size() > 0)
1381 // Remove block from sent history
1382 client->SetBlocksNotSent(modified_blocks);
1387 // Periodically print some info
1389 float &counter = m_print_info_timer;
1395 JMutexAutoLock lock2(m_con_mutex);
1397 for(core::map<u16, RemoteClient*>::Iterator
1398 i = m_clients.getIterator();
1399 i.atEnd() == false; i++)
1401 //u16 peer_id = i.getNode()->getKey();
1402 RemoteClient *client = i.getNode()->getValue();
1403 Player *player = m_env.getPlayer(client->peer_id);
1406 std::cout<<player->getName()<<"\t";
1407 client->PrintInfo(std::cout);
1412 //if(g_settings.getBool("enable_experimental"))
1416 Check added and deleted active objects
1419 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1420 JMutexAutoLock envlock(m_env_mutex);
1421 JMutexAutoLock conlock(m_con_mutex);
1423 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1425 // Radius inside which objects are active
1428 for(core::map<u16, RemoteClient*>::Iterator
1429 i = m_clients.getIterator();
1430 i.atEnd() == false; i++)
1432 RemoteClient *client = i.getNode()->getValue();
1433 Player *player = m_env.getPlayer(client->peer_id);
1436 // This can happen if the client timeouts somehow
1437 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1439 <<" has no associated player"<<std::endl;*/
1442 v3s16 pos = floatToInt(player->getPosition(), BS);
1444 core::map<u16, bool> removed_objects;
1445 core::map<u16, bool> added_objects;
1446 m_env.getRemovedActiveObjects(pos, radius,
1447 client->m_known_objects, removed_objects);
1448 m_env.getAddedActiveObjects(pos, radius,
1449 client->m_known_objects, added_objects);
1451 // Ignore if nothing happened
1452 if(removed_objects.size() == 0 && added_objects.size() == 0)
1454 //dstream<<"INFO: active objects: none changed"<<std::endl;
1458 std::string data_buffer;
1462 // Handle removed objects
1463 writeU16((u8*)buf, removed_objects.size());
1464 data_buffer.append(buf, 2);
1465 for(core::map<u16, bool>::Iterator
1466 i = removed_objects.getIterator();
1467 i.atEnd()==false; i++)
1470 u16 id = i.getNode()->getKey();
1471 ServerActiveObject* obj = m_env.getActiveObject(id);
1473 // Add to data buffer for sending
1474 writeU16((u8*)buf, i.getNode()->getKey());
1475 data_buffer.append(buf, 2);
1477 // Remove from known objects
1478 client->m_known_objects.remove(i.getNode()->getKey());
1480 if(obj && obj->m_known_by_count > 0)
1481 obj->m_known_by_count--;
1484 // Handle added objects
1485 writeU16((u8*)buf, added_objects.size());
1486 data_buffer.append(buf, 2);
1487 for(core::map<u16, bool>::Iterator
1488 i = added_objects.getIterator();
1489 i.atEnd()==false; i++)
1492 u16 id = i.getNode()->getKey();
1493 ServerActiveObject* obj = m_env.getActiveObject(id);
1496 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1498 dstream<<"WARNING: "<<__FUNCTION_NAME
1499 <<": NULL object"<<std::endl;
1501 type = obj->getType();
1503 // Add to data buffer for sending
1504 writeU16((u8*)buf, id);
1505 data_buffer.append(buf, 2);
1506 writeU8((u8*)buf, type);
1507 data_buffer.append(buf, 1);
1510 data_buffer.append(serializeLongString(
1511 obj->getClientInitializationData()));
1513 data_buffer.append(serializeLongString(""));
1515 // Add to known objects
1516 client->m_known_objects.insert(i.getNode()->getKey(), false);
1519 obj->m_known_by_count++;
1523 SharedBuffer<u8> reply(2 + data_buffer.size());
1524 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1525 memcpy((char*)&reply[2], data_buffer.c_str(),
1526 data_buffer.size());
1528 m_con.Send(client->peer_id, 0, reply, true);
1530 dstream<<"INFO: Server: Sent object remove/add: "
1531 <<removed_objects.size()<<" removed, "
1532 <<added_objects.size()<<" added, "
1533 <<"packet size is "<<reply.getSize()<<std::endl;
1538 Collect a list of all the objects known by the clients
1539 and report it back to the environment.
1542 core::map<u16, bool> all_known_objects;
1544 for(core::map<u16, RemoteClient*>::Iterator
1545 i = m_clients.getIterator();
1546 i.atEnd() == false; i++)
1548 RemoteClient *client = i.getNode()->getValue();
1549 // Go through all known objects of client
1550 for(core::map<u16, bool>::Iterator
1551 i = client->m_known_objects.getIterator();
1552 i.atEnd()==false; i++)
1554 u16 id = i.getNode()->getKey();
1555 all_known_objects[id] = true;
1559 m_env.setKnownActiveObjects(whatever);
1565 Send object messages
1568 JMutexAutoLock envlock(m_env_mutex);
1569 JMutexAutoLock conlock(m_con_mutex);
1571 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1574 // Value = data sent by object
1575 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1577 // Get active object messages from environment
1580 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1584 core::list<ActiveObjectMessage>* message_list = NULL;
1585 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1586 n = buffered_messages.find(aom.id);
1589 message_list = new core::list<ActiveObjectMessage>;
1590 buffered_messages.insert(aom.id, message_list);
1594 message_list = n->getValue();
1596 message_list->push_back(aom);
1599 // Route data to every client
1600 for(core::map<u16, RemoteClient*>::Iterator
1601 i = m_clients.getIterator();
1602 i.atEnd()==false; i++)
1604 RemoteClient *client = i.getNode()->getValue();
1605 std::string reliable_data;
1606 std::string unreliable_data;
1607 // Go through all objects in message buffer
1608 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1609 j = buffered_messages.getIterator();
1610 j.atEnd()==false; j++)
1612 // If object is not known by client, skip it
1613 u16 id = j.getNode()->getKey();
1614 if(client->m_known_objects.find(id) == NULL)
1616 // Get message list of object
1617 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1618 // Go through every message
1619 for(core::list<ActiveObjectMessage>::Iterator
1620 k = list->begin(); k != list->end(); k++)
1622 // Compose the full new data with header
1623 ActiveObjectMessage aom = *k;
1624 std::string new_data;
1627 writeU16((u8*)&buf[0], aom.id);
1628 new_data.append(buf, 2);
1630 new_data += serializeString(aom.datastring);
1631 // Add data to buffer
1633 reliable_data += new_data;
1635 unreliable_data += new_data;
1639 reliable_data and unreliable_data are now ready.
1642 if(reliable_data.size() > 0)
1644 SharedBuffer<u8> reply(2 + reliable_data.size());
1645 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1646 memcpy((char*)&reply[2], reliable_data.c_str(),
1647 reliable_data.size());
1649 m_con.Send(client->peer_id, 0, reply, true);
1651 if(unreliable_data.size() > 0)
1653 SharedBuffer<u8> reply(2 + unreliable_data.size());
1654 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1655 memcpy((char*)&reply[2], unreliable_data.c_str(),
1656 unreliable_data.size());
1657 // Send as unreliable
1658 m_con.Send(client->peer_id, 0, reply, false);
1661 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1663 dstream<<"INFO: Server: Size of object message data: "
1664 <<"reliable: "<<reliable_data.size()
1665 <<", unreliable: "<<unreliable_data.size()
1670 // Clear buffered_messages
1671 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1672 i = buffered_messages.getIterator();
1673 i.atEnd()==false; i++)
1675 delete i.getNode()->getValue();
1679 } // enable_experimental
1682 Send queued-for-sending map edit events.
1685 // Don't send too many at a time
1688 // Single change sending is disabled if queue size is not small
1689 bool disable_single_change_sending = false;
1690 if(m_unsent_map_edit_queue.size() >= 4)
1691 disable_single_change_sending = true;
1693 bool got_any_events = false;
1695 // We'll log the amount of each
1698 while(m_unsent_map_edit_queue.size() != 0)
1700 got_any_events = true;
1702 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1704 // Players far away from the change are stored here.
1705 // Instead of sending the changes, MapBlocks are set not sent
1707 core::list<u16> far_players;
1709 if(event->type == MEET_ADDNODE)
1711 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1712 prof.add("MEET_ADDNODE", 1);
1713 if(disable_single_change_sending)
1714 sendAddNode(event->p, event->n, event->already_known_by_peer,
1717 sendAddNode(event->p, event->n, event->already_known_by_peer,
1720 else if(event->type == MEET_REMOVENODE)
1722 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1723 prof.add("MEET_REMOVENODE", 1);
1724 if(disable_single_change_sending)
1725 sendRemoveNode(event->p, event->already_known_by_peer,
1728 sendRemoveNode(event->p, event->already_known_by_peer,
1731 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1733 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1734 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1735 setBlockNotSent(event->p);
1737 else if(event->type == MEET_OTHER)
1739 dstream<<"Server: MEET_OTHER"<<std::endl;
1740 prof.add("MEET_OTHER", 1);
1741 for(core::map<v3s16, bool>::Iterator
1742 i = event->modified_blocks.getIterator();
1743 i.atEnd()==false; i++)
1745 v3s16 p = i.getNode()->getKey();
1751 prof.add("unknown", 1);
1752 dstream<<"WARNING: Server: Unknown MapEditEvent "
1753 <<((u32)event->type)<<std::endl;
1757 Set blocks not sent to far players
1759 if(far_players.size() > 0)
1761 // Convert list format to that wanted by SetBlocksNotSent
1762 core::map<v3s16, MapBlock*> modified_blocks2;
1763 for(core::map<v3s16, bool>::Iterator
1764 i = event->modified_blocks.getIterator();
1765 i.atEnd()==false; i++)
1767 v3s16 p = i.getNode()->getKey();
1768 modified_blocks2.insert(p,
1769 m_env.getMap().getBlockNoCreateNoEx(p));
1771 // Set blocks not sent
1772 for(core::list<u16>::Iterator
1773 i = far_players.begin();
1774 i != far_players.end(); i++)
1777 RemoteClient *client = getClient(peer_id);
1780 client->SetBlocksNotSent(modified_blocks2);
1786 /*// Don't send too many at a time
1788 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1794 dstream<<"Server: MapEditEvents:"<<std::endl;
1795 prof.print(dstream);
1801 Send object positions
1802 TODO: Get rid of MapBlockObjects
1805 float &counter = m_objectdata_timer;
1807 if(counter >= g_settings.getFloat("objectdata_interval"))
1809 JMutexAutoLock lock1(m_env_mutex);
1810 JMutexAutoLock lock2(m_con_mutex);
1812 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1814 SendObjectData(counter);
1821 Trigger emergethread (it somehow gets to a non-triggered but
1822 bysy state sometimes)
1825 float &counter = m_emergethread_trigger_timer;
1831 m_emergethread.trigger();
1835 // Save map, players and auth stuff
1837 float &counter = m_savemap_timer;
1839 if(counter >= g_settings.getFloat("server_map_save_interval"))
1843 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1846 if(m_authmanager.isModified())
1847 m_authmanager.save();
1850 if(m_banmanager.isModified())
1851 m_banmanager.save();
1854 JMutexAutoLock lock(m_env_mutex);
1856 /*// Unload unused data (delete from memory)
1857 m_env.getMap().unloadUnusedData(
1858 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1860 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1861 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1864 // Save only changed parts
1865 m_env.getMap().save(true);
1867 /*if(deleted_count > 0)
1869 dout_server<<"Server: Unloaded "<<deleted_count
1870 <<" blocks from memory"<<std::endl;
1874 m_env.serializePlayers(m_mapsavedir);
1876 // Save environment metadata
1877 m_env.saveMeta(m_mapsavedir);
1882 void Server::Receive()
1884 DSTACK(__FUNCTION_NAME);
1885 u32 data_maxsize = 10000;
1886 Buffer<u8> data(data_maxsize);
1891 JMutexAutoLock conlock(m_con_mutex);
1892 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1895 // This has to be called so that the client list gets synced
1896 // with the peer list of the connection
1897 handlePeerChanges();
1899 ProcessData(*data, datasize, peer_id);
1901 catch(con::InvalidIncomingDataException &e)
1903 derr_server<<"Server::Receive(): "
1904 "InvalidIncomingDataException: what()="
1905 <<e.what()<<std::endl;
1907 catch(con::PeerNotFoundException &e)
1909 //NOTE: This is not needed anymore
1911 // The peer has been disconnected.
1912 // Find the associated player and remove it.
1914 /*JMutexAutoLock envlock(m_env_mutex);
1916 dout_server<<"ServerThread: peer_id="<<peer_id
1917 <<" has apparently closed connection. "
1918 <<"Removing player."<<std::endl;
1920 m_env.removePlayer(peer_id);*/
1924 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1926 DSTACK(__FUNCTION_NAME);
1927 // Environment is locked first.
1928 JMutexAutoLock envlock(m_env_mutex);
1929 JMutexAutoLock conlock(m_con_mutex);
1933 peer = m_con.GetPeer(peer_id);
1935 catch(con::PeerNotFoundException &e)
1937 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1938 <<peer_id<<" not found"<<std::endl;
1942 // drop player if is ip is banned
1943 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1944 SendAccessDenied(m_con, peer_id,
1945 L"Your ip is banned. Banned name was "
1946 +narrow_to_wide(m_banmanager.getBanName(
1947 peer->address.serializeString())));
1948 m_con.deletePeer(peer_id, false);
1952 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1960 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1962 if(command == TOSERVER_INIT)
1964 // [0] u16 TOSERVER_INIT
1965 // [2] u8 SER_FMT_VER_HIGHEST
1966 // [3] u8[20] player_name
1967 // [23] u8[28] password <--- can be sent without this, from old versions
1969 if(datasize < 2+1+PLAYERNAME_SIZE)
1972 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1973 <<peer->id<<std::endl;
1975 // First byte after command is maximum supported
1976 // serialization version
1977 u8 client_max = data[2];
1978 u8 our_max = SER_FMT_VER_HIGHEST;
1979 // Use the highest version supported by both
1980 u8 deployed = core::min_(client_max, our_max);
1981 // If it's lower than the lowest supported, give up.
1982 if(deployed < SER_FMT_VER_LOWEST)
1983 deployed = SER_FMT_VER_INVALID;
1985 //peer->serialization_version = deployed;
1986 getClient(peer->id)->pending_serialization_version = deployed;
1988 if(deployed == SER_FMT_VER_INVALID)
1990 derr_server<<DTIME<<"Server: Cannot negotiate "
1991 "serialization version with peer "
1992 <<peer_id<<std::endl;
1993 SendAccessDenied(m_con, peer_id,
1994 L"Your client is too old (map format)");
1999 Read and check network protocol version
2002 u16 net_proto_version = 0;
2003 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2005 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2008 getClient(peer->id)->net_proto_version = net_proto_version;
2010 if(net_proto_version == 0)
2012 SendAccessDenied(m_con, peer_id,
2013 L"Your client is too old. Please upgrade.");
2017 /* Uhh... this should actually be a warning but let's do it like this */
2018 if(net_proto_version < 2)
2020 SendAccessDenied(m_con, peer_id,
2021 L"Your client is too old. Please upgrade.");
2030 char playername[PLAYERNAME_SIZE];
2031 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2033 playername[i] = data[3+i];
2035 playername[PLAYERNAME_SIZE-1] = 0;
2037 if(playername[0]=='\0')
2039 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
2040 SendAccessDenied(m_con, peer_id,
2045 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2047 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
2048 SendAccessDenied(m_con, peer_id,
2049 L"Name contains unallowed characters");
2054 char password[PASSWORD_SIZE];
2055 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2057 // old version - assume blank password
2062 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2064 password[i] = data[23+i];
2066 password[PASSWORD_SIZE-1] = 0;
2069 std::string checkpwd;
2070 if(m_authmanager.exists(playername))
2072 checkpwd = m_authmanager.getPassword(playername);
2076 checkpwd = g_settings.get("default_password");
2079 /*dstream<<"Server: Client gave password '"<<password
2080 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2082 if(password != checkpwd && m_authmanager.exists(playername))
2084 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2085 <<": supplied invalid password for "
2086 <<playername<<std::endl;
2087 SendAccessDenied(m_con, peer_id, L"Invalid password");
2091 // Add player to auth manager
2092 if(m_authmanager.exists(playername) == false)
2094 derr_server<<DTIME<<"Server: adding player "<<playername
2095 <<" to auth manager"<<std::endl;
2096 m_authmanager.add(playername);
2097 m_authmanager.setPassword(playername, checkpwd);
2098 m_authmanager.setPrivs(playername,
2099 stringToPrivs(g_settings.get("default_privs")));
2100 m_authmanager.save();
2103 // Enforce user limit.
2104 // Don't enforce for users that have some admin right
2105 if(m_clients.size() >= g_settings.getU16("max_users") &&
2106 (m_authmanager.getPrivs(playername)
2107 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2108 playername != g_settings.get("name"))
2110 SendAccessDenied(m_con, peer_id, L"Too many users.");
2115 Player *player = emergePlayer(playername, password, peer_id);
2118 // DEBUG: Test serialization
2119 std::ostringstream test_os;
2120 player->serialize(test_os);
2121 dstream<<"Player serialization test: \""<<test_os.str()
2123 std::istringstream test_is(test_os.str());
2124 player->deSerialize(test_is);
2127 // If failed, cancel
2130 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2131 <<": failed to emerge player"<<std::endl;
2136 // If a client is already connected to the player, cancel
2137 if(player->peer_id != 0)
2139 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2140 <<" tried to connect to "
2141 "an already connected player (peer_id="
2142 <<player->peer_id<<")"<<std::endl;
2145 // Set client of player
2146 player->peer_id = peer_id;
2149 // Check if player doesn't exist
2151 throw con::InvalidIncomingDataException
2152 ("Server::ProcessData(): INIT: Player doesn't exist");
2154 /*// update name if it was supplied
2155 if(datasize >= 20+3)
2158 player->updateName((const char*)&data[3]);
2162 Answer with a TOCLIENT_INIT
2165 SharedBuffer<u8> reply(2+1+6+8);
2166 writeU16(&reply[0], TOCLIENT_INIT);
2167 writeU8(&reply[2], deployed);
2168 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2169 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2172 m_con.Send(peer_id, 0, reply, true);
2176 Send complete position information
2178 SendMovePlayer(player);
2183 if(command == TOSERVER_INIT2)
2185 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2186 <<peer->id<<std::endl;
2189 getClient(peer->id)->serialization_version
2190 = getClient(peer->id)->pending_serialization_version;
2193 Send some initialization data
2196 // Send player info to all players
2199 // Send inventory to player
2200 UpdateCrafting(peer->id);
2201 SendInventory(peer->id);
2203 // Send player items to all players
2208 Player *player = m_env.getPlayer(peer_id);
2209 SendPlayerHP(player);
2214 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2215 m_env.getTimeOfDay());
2216 m_con.Send(peer->id, 0, data, true);
2219 // Send information about server to player in chat
2220 SendChatMessage(peer_id, getStatusString());
2222 // Send information about joining in chat
2224 std::wstring name = L"unknown";
2225 Player *player = m_env.getPlayer(peer_id);
2227 name = narrow_to_wide(player->getName());
2229 std::wstring message;
2232 message += L" joined game";
2233 BroadcastChatMessage(message);
2236 // Warnings about protocol version can be issued here
2237 /*if(getClient(peer->id)->net_proto_version == 0)
2239 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2245 if(peer_ser_ver == SER_FMT_VER_INVALID)
2247 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2248 " serialization format invalid or not initialized."
2249 " Skipping incoming command="<<command<<std::endl;
2253 Player *player = m_env.getPlayer(peer_id);
2256 derr_server<<"Server::ProcessData(): Cancelling: "
2257 "No player for peer_id="<<peer_id
2261 if(command == TOSERVER_PLAYERPOS)
2263 if(datasize < 2+12+12+4+4)
2267 v3s32 ps = readV3S32(&data[start+2]);
2268 v3s32 ss = readV3S32(&data[start+2+12]);
2269 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2270 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2271 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2272 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2273 pitch = wrapDegrees(pitch);
2274 yaw = wrapDegrees(yaw);
2275 player->setPosition(position);
2276 player->setSpeed(speed);
2277 player->setPitch(pitch);
2278 player->setYaw(yaw);
2280 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2281 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2282 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2284 else if(command == TOSERVER_GOTBLOCKS)
2297 u16 count = data[2];
2298 for(u16 i=0; i<count; i++)
2300 if((s16)datasize < 2+1+(i+1)*6)
2301 throw con::InvalidIncomingDataException
2302 ("GOTBLOCKS length is too short");
2303 v3s16 p = readV3S16(&data[2+1+i*6]);
2304 /*dstream<<"Server: GOTBLOCKS ("
2305 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2306 RemoteClient *client = getClient(peer_id);
2307 client->GotBlock(p);
2310 else if(command == TOSERVER_DELETEDBLOCKS)
2323 u16 count = data[2];
2324 for(u16 i=0; i<count; i++)
2326 if((s16)datasize < 2+1+(i+1)*6)
2327 throw con::InvalidIncomingDataException
2328 ("DELETEDBLOCKS length is too short");
2329 v3s16 p = readV3S16(&data[2+1+i*6]);
2330 /*dstream<<"Server: DELETEDBLOCKS ("
2331 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2332 RemoteClient *client = getClient(peer_id);
2333 client->SetBlockNotSent(p);
2336 else if(command == TOSERVER_CLICK_OBJECT)
2341 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2346 [2] u8 button (0=left, 1=right)
2351 u8 button = readU8(&data[2]);
2353 p.X = readS16(&data[3]);
2354 p.Y = readS16(&data[5]);
2355 p.Z = readS16(&data[7]);
2356 s16 id = readS16(&data[9]);
2357 //u16 item_i = readU16(&data[11]);
2359 MapBlock *block = NULL;
2362 block = m_env.getMap().getBlockNoCreate(p);
2364 catch(InvalidPositionException &e)
2366 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2370 MapBlockObject *obj = block->getObject(id);
2374 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2378 //TODO: Check that object is reasonably close
2383 InventoryList *ilist = player->inventory.getList("main");
2384 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2387 // Skip if inventory has no free space
2388 if(ilist->getUsedSlots() == ilist->getSize())
2390 dout_server<<"Player inventory has no free space"<<std::endl;
2395 Create the inventory item
2397 InventoryItem *item = NULL;
2398 // If it is an item-object, take the item from it
2399 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2401 item = ((ItemObject*)obj)->createInventoryItem();
2403 // Else create an item of the object
2406 item = new MapBlockObjectItem
2407 (obj->getInventoryString());
2410 // Add to inventory and send inventory
2411 ilist->addItem(item);
2412 UpdateCrafting(player->peer_id);
2413 SendInventory(player->peer_id);
2416 // Remove from block
2417 block->removeObject(id);
2420 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2425 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2431 [2] u8 button (0=left, 1=right)
2435 u8 button = readU8(&data[2]);
2436 u16 id = readS16(&data[3]);
2437 u16 item_i = readU16(&data[11]);
2439 ServerActiveObject *obj = m_env.getActiveObject(id);
2443 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2448 // Skip if object has been removed
2452 //TODO: Check that object is reasonably close
2454 // Left click, pick object up (usually)
2458 Try creating inventory item
2460 InventoryItem *item = obj->createPickedUpItem();
2464 InventoryList *ilist = player->inventory.getList("main");
2467 if(g_settings.getBool("creative_mode") == false)
2469 // Skip if inventory has no free space
2470 if(ilist->roomForItem(item) == false)
2472 dout_server<<"Player inventory has no free space"<<std::endl;
2476 // Add to inventory and send inventory
2477 ilist->addItem(item);
2478 UpdateCrafting(player->peer_id);
2479 SendInventory(player->peer_id);
2482 // Remove object from environment
2483 obj->m_removed = true;
2489 Item cannot be picked up. Punch it instead.
2492 ToolItem *titem = NULL;
2493 std::string toolname = "";
2495 InventoryList *mlist = player->inventory.getList("main");
2498 InventoryItem *item = mlist->getItem(item_i);
2499 if(item && (std::string)item->getName() == "ToolItem")
2501 titem = (ToolItem*)item;
2502 toolname = titem->getToolName();
2506 v3f playerpos = player->getPosition();
2507 v3f objpos = obj->getBasePosition();
2508 v3f dir = (objpos - playerpos).normalize();
2510 u16 wear = obj->punch(toolname, dir);
2514 bool weared_out = titem->addWear(wear);
2516 mlist->deleteItem(item_i);
2517 SendInventory(player->peer_id);
2521 // Right click, do something with object
2524 // Track hp changes super-crappily
2525 u16 oldhp = player->hp;
2528 obj->rightClick(player);
2531 if(player->hp != oldhp)
2533 SendPlayerHP(player);
2537 else if(command == TOSERVER_GROUND_ACTION)
2545 [3] v3s16 nodepos_undersurface
2546 [9] v3s16 nodepos_abovesurface
2551 2: stop digging (all parameters ignored)
2552 3: digging completed
2554 u8 action = readU8(&data[2]);
2556 p_under.X = readS16(&data[3]);
2557 p_under.Y = readS16(&data[5]);
2558 p_under.Z = readS16(&data[7]);
2560 p_over.X = readS16(&data[9]);
2561 p_over.Y = readS16(&data[11]);
2562 p_over.Z = readS16(&data[13]);
2563 u16 item_i = readU16(&data[15]);
2565 //TODO: Check that target is reasonably close
2573 NOTE: This can be used in the future to check if
2574 somebody is cheating, by checking the timing.
2581 else if(action == 2)
2584 RemoteClient *client = getClient(peer->id);
2585 JMutexAutoLock digmutex(client->m_dig_mutex);
2586 client->m_dig_tool_item = -1;
2591 3: Digging completed
2593 else if(action == 3)
2595 // Mandatory parameter; actually used for nothing
2596 core::map<v3s16, MapBlock*> modified_blocks;
2598 content_t material = CONTENT_IGNORE;
2599 u8 mineral = MINERAL_NONE;
2601 bool cannot_remove_node = false;
2605 MapNode n = m_env.getMap().getNode(p_under);
2607 mineral = n.getMineral();
2608 // Get material at position
2609 material = n.getContent();
2610 // If not yet cancelled
2611 if(cannot_remove_node == false)
2613 // If it's not diggable, do nothing
2614 if(content_diggable(material) == false)
2616 derr_server<<"Server: Not finishing digging: "
2617 <<"Node not diggable"
2619 cannot_remove_node = true;
2622 // If not yet cancelled
2623 if(cannot_remove_node == false)
2625 // Get node metadata
2626 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2627 if(meta && meta->nodeRemovalDisabled() == true)
2629 derr_server<<"Server: Not finishing digging: "
2630 <<"Node metadata disables removal"
2632 cannot_remove_node = true;
2636 catch(InvalidPositionException &e)
2638 derr_server<<"Server: Not finishing digging: Node not found."
2639 <<" Adding block to emerge queue."
2641 m_emerge_queue.addBlock(peer_id,
2642 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2643 cannot_remove_node = true;
2646 // Make sure the player is allowed to do it
2647 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2649 dstream<<"Player "<<player->getName()<<" cannot remove node"
2650 <<" because privileges are "<<getPlayerPrivs(player)
2652 cannot_remove_node = true;
2656 If node can't be removed, set block to be re-sent to
2659 if(cannot_remove_node)
2661 derr_server<<"Server: Not finishing digging."<<std::endl;
2663 // Client probably has wrong data.
2664 // Set block not sent, so that client will get
2666 dstream<<"Client "<<peer_id<<" tried to dig "
2667 <<"node; but node cannot be removed."
2668 <<" setting MapBlock not sent."<<std::endl;
2669 RemoteClient *client = getClient(peer_id);
2670 v3s16 blockpos = getNodeBlockPos(p_under);
2671 client->SetBlockNotSent(blockpos);
2677 Send the removal to all close-by players.
2678 - If other player is close, send REMOVENODE
2679 - Otherwise set blocks not sent
2681 core::list<u16> far_players;
2682 sendRemoveNode(p_under, peer_id, &far_players, 30);
2685 Update and send inventory
2688 if(g_settings.getBool("creative_mode") == false)
2693 InventoryList *mlist = player->inventory.getList("main");
2696 InventoryItem *item = mlist->getItem(item_i);
2697 if(item && (std::string)item->getName() == "ToolItem")
2699 ToolItem *titem = (ToolItem*)item;
2700 std::string toolname = titem->getToolName();
2702 // Get digging properties for material and tool
2703 DiggingProperties prop =
2704 getDiggingProperties(material, toolname);
2706 if(prop.diggable == false)
2708 derr_server<<"Server: WARNING: Player digged"
2709 <<" with impossible material + tool"
2710 <<" combination"<<std::endl;
2713 bool weared_out = titem->addWear(prop.wear);
2717 mlist->deleteItem(item_i);
2723 Add dug item to inventory
2726 InventoryItem *item = NULL;
2728 if(mineral != MINERAL_NONE)
2729 item = getDiggedMineralItem(mineral);
2734 std::string &dug_s = content_features(material).dug_item;
2737 std::istringstream is(dug_s, std::ios::binary);
2738 item = InventoryItem::deSerialize(is);
2744 // Add a item to inventory
2745 player->inventory.addItem("main", item);
2748 UpdateCrafting(player->peer_id);
2749 SendInventory(player->peer_id);
2755 (this takes some time so it is done after the quick stuff)
2758 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2760 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2763 Set blocks not sent to far players
2765 for(core::list<u16>::Iterator
2766 i = far_players.begin();
2767 i != far_players.end(); i++)
2770 RemoteClient *client = getClient(peer_id);
2773 client->SetBlocksNotSent(modified_blocks);
2780 else if(action == 1)
2783 InventoryList *ilist = player->inventory.getList("main");
2788 InventoryItem *item = ilist->getItem(item_i);
2790 // If there is no item, it is not possible to add it anywhere
2795 Handle material items
2797 if(std::string("MaterialItem") == item->getName())
2800 // Don't add a node if this is not a free space
2801 MapNode n2 = m_env.getMap().getNode(p_over);
2802 bool no_enough_privs =
2803 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2805 dstream<<"Player "<<player->getName()<<" cannot add node"
2806 <<" because privileges are "<<getPlayerPrivs(player)
2809 if(content_features(n2).buildable_to == false
2812 // Client probably has wrong data.
2813 // Set block not sent, so that client will get
2815 dstream<<"Client "<<peer_id<<" tried to place"
2816 <<" node in invalid position; setting"
2817 <<" MapBlock not sent."<<std::endl;
2818 RemoteClient *client = getClient(peer_id);
2819 v3s16 blockpos = getNodeBlockPos(p_over);
2820 client->SetBlockNotSent(blockpos);
2824 catch(InvalidPositionException &e)
2826 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2827 <<" Adding block to emerge queue."
2829 m_emerge_queue.addBlock(peer_id,
2830 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2834 // Reset build time counter
2835 getClient(peer->id)->m_time_from_building = 0.0;
2838 MaterialItem *mitem = (MaterialItem*)item;
2840 n.setContent(mitem->getMaterial());
2842 // Calculate direction for wall mounted stuff
2843 if(content_features(n).wall_mounted)
2844 n.param2 = packDir(p_under - p_over);
2846 // Calculate the direction for furnaces and chests and stuff
2847 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2849 v3f playerpos = player->getPosition();
2850 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2851 blockpos = blockpos.normalize();
2853 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2867 Send to all close-by players
2869 core::list<u16> far_players;
2870 sendAddNode(p_over, n, 0, &far_players, 30);
2875 InventoryList *ilist = player->inventory.getList("main");
2876 if(g_settings.getBool("creative_mode") == false && ilist)
2878 // Remove from inventory and send inventory
2879 if(mitem->getCount() == 1)
2880 ilist->deleteItem(item_i);
2884 UpdateCrafting(peer_id);
2885 SendInventory(peer_id);
2891 This takes some time so it is done after the quick stuff
2893 core::map<v3s16, MapBlock*> modified_blocks;
2895 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2897 std::string p_name = std::string(player->getName());
2898 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2901 Set blocks not sent to far players
2903 for(core::list<u16>::Iterator
2904 i = far_players.begin();
2905 i != far_players.end(); i++)
2908 RemoteClient *client = getClient(peer_id);
2911 client->SetBlocksNotSent(modified_blocks);
2915 Calculate special events
2918 /*if(n.d == CONTENT_MESE)
2921 for(s16 z=-1; z<=1; z++)
2922 for(s16 y=-1; y<=1; y++)
2923 for(s16 x=-1; x<=1; x++)
2930 Place other item (not a block)
2934 v3s16 blockpos = getNodeBlockPos(p_over);
2937 Check that the block is loaded so that the item
2938 can properly be added to the static list too
2940 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2943 derr_server<<"Error while placing object: "
2944 "block not found"<<std::endl;
2949 If in creative mode, item dropping is disabled unless
2950 player has build privileges
2952 if(g_settings.getBool("creative_mode") &&
2953 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2955 derr_server<<"Not allowing player to drop item: "
2956 "creative mode and no build privs"<<std::endl;
2960 dout_server<<"Placing a miscellaneous item on map"
2963 // Calculate a position for it
2964 v3f pos = intToFloat(p_over, BS);
2966 pos.Y -= BS*0.25; // let it drop a bit
2968 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2969 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2974 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2978 derr_server<<"WARNING: item resulted in NULL object, "
2979 <<"not placing onto map"
2984 // Add the object to the environment
2985 m_env.addActiveObject(obj);
2987 dout_server<<"Placed object"<<std::endl;
2989 if(g_settings.getBool("creative_mode") == false)
2991 // Delete the right amount of items from the slot
2992 u16 dropcount = item->getDropCount();
2994 // Delete item if all gone
2995 if(item->getCount() <= dropcount)
2997 if(item->getCount() < dropcount)
2998 dstream<<"WARNING: Server: dropped more items"
2999 <<" than the slot contains"<<std::endl;
3001 InventoryList *ilist = player->inventory.getList("main");
3003 // Remove from inventory and send inventory
3004 ilist->deleteItem(item_i);
3006 // Else decrement it
3008 item->remove(dropcount);
3011 UpdateCrafting(peer_id);
3012 SendInventory(peer_id);
3020 Catch invalid actions
3024 derr_server<<"WARNING: Server: Invalid action "
3025 <<action<<std::endl;
3029 else if(command == TOSERVER_RELEASE)
3038 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3041 else if(command == TOSERVER_SIGNTEXT)
3043 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3052 std::string datastring((char*)&data[2], datasize-2);
3053 std::istringstream is(datastring, std::ios_base::binary);
3056 is.read((char*)buf, 6);
3057 v3s16 blockpos = readV3S16(buf);
3058 is.read((char*)buf, 2);
3059 s16 id = readS16(buf);
3060 is.read((char*)buf, 2);
3061 u16 textlen = readU16(buf);
3063 for(u16 i=0; i<textlen; i++)
3065 is.read((char*)buf, 1);
3066 text += (char)buf[0];
3069 MapBlock *block = NULL;
3072 block = m_env.getMap().getBlockNoCreate(blockpos);
3074 catch(InvalidPositionException &e)
3076 derr_server<<"Error while setting sign text: "
3077 "block not found"<<std::endl;
3081 MapBlockObject *obj = block->getObject(id);
3084 derr_server<<"Error while setting sign text: "
3085 "object not found"<<std::endl;
3089 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3091 derr_server<<"Error while setting sign text: "
3092 "object is not a sign"<<std::endl;
3096 ((SignObject*)obj)->setText(text);
3098 obj->getBlock()->setChangedFlag();
3100 else if(command == TOSERVER_SIGNNODETEXT)
3102 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3110 std::string datastring((char*)&data[2], datasize-2);
3111 std::istringstream is(datastring, std::ios_base::binary);
3114 is.read((char*)buf, 6);
3115 v3s16 p = readV3S16(buf);
3116 is.read((char*)buf, 2);
3117 u16 textlen = readU16(buf);
3119 for(u16 i=0; i<textlen; i++)
3121 is.read((char*)buf, 1);
3122 text += (char)buf[0];
3125 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3128 if(meta->typeId() != CONTENT_SIGN_WALL)
3130 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3131 signmeta->setText(text);
3133 v3s16 blockpos = getNodeBlockPos(p);
3134 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3137 block->setChangedFlag();
3140 for(core::map<u16, RemoteClient*>::Iterator
3141 i = m_clients.getIterator();
3142 i.atEnd()==false; i++)
3144 RemoteClient *client = i.getNode()->getValue();
3145 client->SetBlockNotSent(blockpos);
3148 else if(command == TOSERVER_INVENTORY_ACTION)
3150 /*// Ignore inventory changes if in creative mode
3151 if(g_settings.getBool("creative_mode") == true)
3153 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3157 // Strip command and create a stream
3158 std::string datastring((char*)&data[2], datasize-2);
3159 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3160 std::istringstream is(datastring, std::ios_base::binary);
3162 InventoryAction *a = InventoryAction::deSerialize(is);
3167 c.current_player = player;
3170 Handle craftresult specially if not in creative mode
3172 bool disable_action = false;
3173 if(a->getType() == IACTION_MOVE
3174 && g_settings.getBool("creative_mode") == false)
3176 IMoveAction *ma = (IMoveAction*)a;
3177 if(ma->to_inv == "current_player" &&
3178 ma->from_inv == "current_player")
3180 InventoryList *rlist = player->inventory.getList("craftresult");
3182 InventoryList *clist = player->inventory.getList("craft");
3184 InventoryList *mlist = player->inventory.getList("main");
3187 Craftresult is no longer preview if something
3190 if(ma->to_list == "craftresult"
3191 && ma->from_list != "craftresult")
3193 // If it currently is a preview, remove
3195 if(player->craftresult_is_preview)
3197 rlist->deleteItem(0);
3199 player->craftresult_is_preview = false;
3202 Crafting takes place if this condition is true.
3204 if(player->craftresult_is_preview &&
3205 ma->from_list == "craftresult")
3207 player->craftresult_is_preview = false;
3208 clist->decrementMaterials(1);
3211 If the craftresult is placed on itself, move it to
3212 main inventory instead of doing the action
3214 if(ma->to_list == "craftresult"
3215 && ma->from_list == "craftresult")
3217 disable_action = true;
3219 InventoryItem *item1 = rlist->changeItem(0, NULL);
3220 mlist->addItem(item1);
3223 // Disallow moving items if not allowed to build
3224 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3228 // if it's a locking chest, only allow the owner or server admins to move items
3229 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3231 Strfnd fn(ma->from_inv);
3232 std::string id0 = fn.next(":");
3233 if(id0 == "nodemeta")
3236 p.X = stoi(fn.next(","));
3237 p.Y = stoi(fn.next(","));
3238 p.Z = stoi(fn.next(","));
3239 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3240 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3241 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3242 if (lcm->getOwner() != player->getName())
3247 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3249 Strfnd fn(ma->to_inv);
3250 std::string id0 = fn.next(":");
3251 if(id0 == "nodemeta")
3254 p.X = stoi(fn.next(","));
3255 p.Y = stoi(fn.next(","));
3256 p.Z = stoi(fn.next(","));
3257 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3258 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3259 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3260 if (lcm->getOwner() != player->getName())
3267 if(disable_action == false)
3269 // Feed action to player inventory
3277 UpdateCrafting(player->peer_id);
3278 SendInventory(player->peer_id);
3283 dstream<<"TOSERVER_INVENTORY_ACTION: "
3284 <<"InventoryAction::deSerialize() returned NULL"
3288 else if(command == TOSERVER_CHAT_MESSAGE)
3296 std::string datastring((char*)&data[2], datasize-2);
3297 std::istringstream is(datastring, std::ios_base::binary);
3300 is.read((char*)buf, 2);
3301 u16 len = readU16(buf);
3303 std::wstring message;
3304 for(u16 i=0; i<len; i++)
3306 is.read((char*)buf, 2);
3307 message += (wchar_t)readU16(buf);
3310 // Get player name of this client
3311 std::wstring name = narrow_to_wide(player->getName());
3313 // Line to send to players
3315 // Whether to send to the player that sent the line
3316 bool send_to_sender = false;
3317 // Whether to send to other players
3318 bool send_to_others = false;
3320 // Local player gets all privileges regardless of
3321 // what's set on their account.
3322 u64 privs = getPlayerPrivs(player);
3325 if(message[0] == L'/')
3327 size_t strip_size = 1;
3328 if (message[1] == L'#') // support old-style commans
3330 message = message.substr(strip_size);
3332 WStrfnd f1(message);
3333 f1.next(L" "); // Skip over /#whatever
3334 std::wstring paramstring = f1.next(L"");
3336 ServerCommandContext *ctx = new ServerCommandContext(
3337 str_split(message, L' '),
3344 std::wstring reply(processServerCommand(ctx));
3345 send_to_sender = ctx->flags & SEND_TO_SENDER;
3346 send_to_others = ctx->flags & SEND_TO_OTHERS;
3348 if (ctx->flags & SEND_NO_PREFIX)
3351 line += L"Server: " + reply;
3358 if(privs & PRIV_SHOUT)
3364 send_to_others = true;
3368 line += L"Server: You are not allowed to shout";
3369 send_to_sender = true;
3375 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3378 Send the message to clients
3380 for(core::map<u16, RemoteClient*>::Iterator
3381 i = m_clients.getIterator();
3382 i.atEnd() == false; i++)
3384 // Get client and check that it is valid
3385 RemoteClient *client = i.getNode()->getValue();
3386 assert(client->peer_id == i.getNode()->getKey());
3387 if(client->serialization_version == SER_FMT_VER_INVALID)
3391 bool sender_selected = (peer_id == client->peer_id);
3392 if(sender_selected == true && send_to_sender == false)
3394 if(sender_selected == false && send_to_others == false)
3397 SendChatMessage(client->peer_id, line);
3401 else if(command == TOSERVER_DAMAGE)
3403 if(g_settings.getBool("enable_damage"))
3405 std::string datastring((char*)&data[2], datasize-2);
3406 std::istringstream is(datastring, std::ios_base::binary);
3407 u8 damage = readU8(is);
3408 if(player->hp > damage)
3410 player->hp -= damage;
3416 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3419 v3f pos = findSpawnPos(m_env.getServerMap());
3420 player->setPosition(pos);
3422 SendMovePlayer(player);
3423 SendPlayerHP(player);
3425 //TODO: Throw items around
3429 SendPlayerHP(player);
3431 else if(command == TOSERVER_PASSWORD)
3434 [0] u16 TOSERVER_PASSWORD
3435 [2] u8[28] old password
3436 [30] u8[28] new password
3439 if(datasize != 2+PASSWORD_SIZE*2)
3441 /*char password[PASSWORD_SIZE];
3442 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3443 password[i] = data[2+i];
3444 password[PASSWORD_SIZE-1] = 0;*/
3446 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3454 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3456 char c = data[2+PASSWORD_SIZE+i];
3462 dstream<<"Server: Client requests a password change from "
3463 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3465 std::string playername = player->getName();
3467 if(m_authmanager.exists(playername) == false)
3469 dstream<<"Server: playername not found in authmanager"<<std::endl;
3470 // Wrong old password supplied!!
3471 SendChatMessage(peer_id, L"playername not found in authmanager");
3475 std::string checkpwd = m_authmanager.getPassword(playername);
3477 if(oldpwd != checkpwd)
3479 dstream<<"Server: invalid old password"<<std::endl;
3480 // Wrong old password supplied!!
3481 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3485 m_authmanager.setPassword(playername, newpwd);
3487 dstream<<"Server: password change successful for "<<playername
3489 SendChatMessage(peer_id, L"Password change successful");
3491 else if (command == TOSERVER_PLAYERITEM)
3496 u16 item = readU16(&data[2]);
3497 player->wieldItem(item);
3498 SendWieldedItem(player);
3502 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3503 "unknown command "<<command<<std::endl;
3507 catch(SendFailedException &e)
3509 derr_server<<"Server::ProcessData(): SendFailedException: "
3515 void Server::onMapEditEvent(MapEditEvent *event)
3517 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3518 if(m_ignore_map_edit_events)
3520 MapEditEvent *e = event->clone();
3521 m_unsent_map_edit_queue.push_back(e);
3524 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3526 if(id == "current_player")
3528 assert(c->current_player);
3529 return &(c->current_player->inventory);
3533 std::string id0 = fn.next(":");
3535 if(id0 == "nodemeta")
3538 p.X = stoi(fn.next(","));
3539 p.Y = stoi(fn.next(","));
3540 p.Z = stoi(fn.next(","));
3541 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3543 return meta->getInventory();
3544 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3545 <<"no metadata found"<<std::endl;
3549 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3552 void Server::inventoryModified(InventoryContext *c, std::string id)
3554 if(id == "current_player")
3556 assert(c->current_player);
3558 UpdateCrafting(c->current_player->peer_id);
3559 SendInventory(c->current_player->peer_id);
3564 std::string id0 = fn.next(":");
3566 if(id0 == "nodemeta")
3569 p.X = stoi(fn.next(","));
3570 p.Y = stoi(fn.next(","));
3571 p.Z = stoi(fn.next(","));
3572 v3s16 blockpos = getNodeBlockPos(p);
3574 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3576 meta->inventoryModified();
3578 for(core::map<u16, RemoteClient*>::Iterator
3579 i = m_clients.getIterator();
3580 i.atEnd()==false; i++)
3582 RemoteClient *client = i.getNode()->getValue();
3583 client->SetBlockNotSent(blockpos);
3589 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3592 core::list<PlayerInfo> Server::getPlayerInfo()
3594 DSTACK(__FUNCTION_NAME);
3595 JMutexAutoLock envlock(m_env_mutex);
3596 JMutexAutoLock conlock(m_con_mutex);
3598 core::list<PlayerInfo> list;
3600 core::list<Player*> players = m_env.getPlayers();
3602 core::list<Player*>::Iterator i;
3603 for(i = players.begin();
3604 i != players.end(); i++)
3608 Player *player = *i;
3611 con::Peer *peer = m_con.GetPeer(player->peer_id);
3612 // Copy info from peer to info struct
3614 info.address = peer->address;
3615 info.avg_rtt = peer->avg_rtt;
3617 catch(con::PeerNotFoundException &e)
3619 // Set dummy peer info
3621 info.address = Address(0,0,0,0,0);
3625 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3626 info.position = player->getPosition();
3628 list.push_back(info);
3635 void Server::peerAdded(con::Peer *peer)
3637 DSTACK(__FUNCTION_NAME);
3638 dout_server<<"Server::peerAdded(): peer->id="
3639 <<peer->id<<std::endl;
3642 c.type = PEER_ADDED;
3643 c.peer_id = peer->id;
3645 m_peer_change_queue.push_back(c);
3648 void Server::deletingPeer(con::Peer *peer, bool timeout)
3650 DSTACK(__FUNCTION_NAME);
3651 dout_server<<"Server::deletingPeer(): peer->id="
3652 <<peer->id<<", timeout="<<timeout<<std::endl;
3655 c.type = PEER_REMOVED;
3656 c.peer_id = peer->id;
3657 c.timeout = timeout;
3658 m_peer_change_queue.push_back(c);
3665 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3667 DSTACK(__FUNCTION_NAME);
3668 std::ostringstream os(std::ios_base::binary);
3670 writeU16(os, TOCLIENT_HP);
3674 std::string s = os.str();
3675 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3677 con.Send(peer_id, 0, data, true);
3680 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3681 const std::wstring &reason)
3683 DSTACK(__FUNCTION_NAME);
3684 std::ostringstream os(std::ios_base::binary);
3686 writeU16(os, TOCLIENT_ACCESS_DENIED);
3687 os<<serializeWideString(reason);
3690 std::string s = os.str();
3691 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3693 con.Send(peer_id, 0, data, true);
3697 Non-static send methods
3700 void Server::SendObjectData(float dtime)
3702 DSTACK(__FUNCTION_NAME);
3704 core::map<v3s16, bool> stepped_blocks;
3706 for(core::map<u16, RemoteClient*>::Iterator
3707 i = m_clients.getIterator();
3708 i.atEnd() == false; i++)
3710 u16 peer_id = i.getNode()->getKey();
3711 RemoteClient *client = i.getNode()->getValue();
3712 assert(client->peer_id == peer_id);
3714 if(client->serialization_version == SER_FMT_VER_INVALID)
3717 client->SendObjectData(this, dtime, stepped_blocks);
3721 void Server::SendPlayerInfos()
3723 DSTACK(__FUNCTION_NAME);
3725 //JMutexAutoLock envlock(m_env_mutex);
3727 // Get connected players
3728 core::list<Player*> players = m_env.getPlayers(true);
3730 u32 player_count = players.getSize();
3731 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3733 SharedBuffer<u8> data(datasize);
3734 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3737 core::list<Player*>::Iterator i;
3738 for(i = players.begin();
3739 i != players.end(); i++)
3741 Player *player = *i;
3743 /*dstream<<"Server sending player info for player with "
3744 "peer_id="<<player->peer_id<<std::endl;*/
3746 writeU16(&data[start], player->peer_id);
3747 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3748 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3749 start += 2+PLAYERNAME_SIZE;
3752 //JMutexAutoLock conlock(m_con_mutex);
3755 m_con.SendToAll(0, data, true);
3758 void Server::SendInventory(u16 peer_id)
3760 DSTACK(__FUNCTION_NAME);
3762 Player* player = m_env.getPlayer(peer_id);
3769 std::ostringstream os;
3770 //os.imbue(std::locale("C"));
3772 player->inventory.serialize(os);
3774 std::string s = os.str();
3776 SharedBuffer<u8> data(s.size()+2);
3777 writeU16(&data[0], TOCLIENT_INVENTORY);
3778 memcpy(&data[2], s.c_str(), s.size());
3781 m_con.Send(peer_id, 0, data, true);
3784 std::string getWieldedItemString(const Player *player)
3786 const InventoryItem *item = player->getWieldItem();
3788 return std::string("");
3789 std::ostringstream os(std::ios_base::binary);
3790 item->serialize(os);
3794 void Server::SendWieldedItem(const Player* player)
3796 DSTACK(__FUNCTION_NAME);
3800 std::ostringstream os(std::ios_base::binary);
3802 writeU16(os, TOCLIENT_PLAYERITEM);
3804 writeU16(os, player->peer_id);
3805 os<<serializeString(getWieldedItemString(player));
3808 std::string s = os.str();
3809 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3811 m_con.SendToAll(0, data, true);
3814 void Server::SendPlayerItems()
3816 DSTACK(__FUNCTION_NAME);
3818 std::ostringstream os(std::ios_base::binary);
3819 core::list<Player *> players = m_env.getPlayers(true);
3821 writeU16(os, TOCLIENT_PLAYERITEM);
3822 writeU16(os, players.size());
3823 core::list<Player *>::Iterator i;
3824 for(i = players.begin(); i != players.end(); ++i)
3827 writeU16(os, p->peer_id);
3828 os<<serializeString(getWieldedItemString(p));
3832 std::string s = os.str();
3833 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3835 m_con.SendToAll(0, data, true);
3838 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3840 DSTACK(__FUNCTION_NAME);
3842 std::ostringstream os(std::ios_base::binary);
3846 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3847 os.write((char*)buf, 2);
3850 writeU16(buf, message.size());
3851 os.write((char*)buf, 2);
3854 for(u32 i=0; i<message.size(); i++)
3858 os.write((char*)buf, 2);
3862 std::string s = os.str();
3863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3865 m_con.Send(peer_id, 0, data, true);
3868 void Server::BroadcastChatMessage(const std::wstring &message)
3870 for(core::map<u16, RemoteClient*>::Iterator
3871 i = m_clients.getIterator();
3872 i.atEnd() == false; i++)
3874 // Get client and check that it is valid
3875 RemoteClient *client = i.getNode()->getValue();
3876 assert(client->peer_id == i.getNode()->getKey());
3877 if(client->serialization_version == SER_FMT_VER_INVALID)
3880 SendChatMessage(client->peer_id, message);
3884 void Server::SendPlayerHP(Player *player)
3886 SendHP(m_con, player->peer_id, player->hp);
3889 void Server::SendMovePlayer(Player *player)
3891 DSTACK(__FUNCTION_NAME);
3892 std::ostringstream os(std::ios_base::binary);
3894 writeU16(os, TOCLIENT_MOVE_PLAYER);
3895 writeV3F1000(os, player->getPosition());
3896 writeF1000(os, player->getPitch());
3897 writeF1000(os, player->getYaw());
3900 v3f pos = player->getPosition();
3901 f32 pitch = player->getPitch();
3902 f32 yaw = player->getYaw();
3903 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3904 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3911 std::string s = os.str();
3912 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3914 m_con.Send(player->peer_id, 0, data, true);
3917 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3918 core::list<u16> *far_players, float far_d_nodes)
3920 float maxd = far_d_nodes*BS;
3921 v3f p_f = intToFloat(p, BS);
3925 SharedBuffer<u8> reply(replysize);
3926 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3927 writeS16(&reply[2], p.X);
3928 writeS16(&reply[4], p.Y);
3929 writeS16(&reply[6], p.Z);
3931 for(core::map<u16, RemoteClient*>::Iterator
3932 i = m_clients.getIterator();
3933 i.atEnd() == false; i++)
3935 // Get client and check that it is valid
3936 RemoteClient *client = i.getNode()->getValue();
3937 assert(client->peer_id == i.getNode()->getKey());
3938 if(client->serialization_version == SER_FMT_VER_INVALID)
3941 // Don't send if it's the same one
3942 if(client->peer_id == ignore_id)
3948 Player *player = m_env.getPlayer(client->peer_id);
3951 // If player is far away, only set modified blocks not sent
3952 v3f player_pos = player->getPosition();
3953 if(player_pos.getDistanceFrom(p_f) > maxd)
3955 far_players->push_back(client->peer_id);
3962 m_con.Send(client->peer_id, 0, reply, true);
3966 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3967 core::list<u16> *far_players, float far_d_nodes)
3969 float maxd = far_d_nodes*BS;
3970 v3f p_f = intToFloat(p, BS);
3972 for(core::map<u16, RemoteClient*>::Iterator
3973 i = m_clients.getIterator();
3974 i.atEnd() == false; i++)
3976 // Get client and check that it is valid
3977 RemoteClient *client = i.getNode()->getValue();
3978 assert(client->peer_id == i.getNode()->getKey());
3979 if(client->serialization_version == SER_FMT_VER_INVALID)
3982 // Don't send if it's the same one
3983 if(client->peer_id == ignore_id)
3989 Player *player = m_env.getPlayer(client->peer_id);
3992 // If player is far away, only set modified blocks not sent
3993 v3f player_pos = player->getPosition();
3994 if(player_pos.getDistanceFrom(p_f) > maxd)
3996 far_players->push_back(client->peer_id);
4003 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4004 SharedBuffer<u8> reply(replysize);
4005 writeU16(&reply[0], TOCLIENT_ADDNODE);
4006 writeS16(&reply[2], p.X);
4007 writeS16(&reply[4], p.Y);
4008 writeS16(&reply[6], p.Z);
4009 n.serialize(&reply[8], client->serialization_version);
4012 m_con.Send(client->peer_id, 0, reply, true);
4016 void Server::setBlockNotSent(v3s16 p)
4018 for(core::map<u16, RemoteClient*>::Iterator
4019 i = m_clients.getIterator();
4020 i.atEnd()==false; i++)
4022 RemoteClient *client = i.getNode()->getValue();
4023 client->SetBlockNotSent(p);
4027 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4029 DSTACK(__FUNCTION_NAME);
4031 v3s16 p = block->getPos();
4035 bool completely_air = true;
4036 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4037 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4038 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4040 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4042 completely_air = false;
4043 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4048 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4050 dstream<<"[completely air] ";
4055 Create a packet with the block in the right format
4058 std::ostringstream os(std::ios_base::binary);
4059 block->serialize(os, ver);
4060 std::string s = os.str();
4061 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4063 u32 replysize = 8 + blockdata.getSize();
4064 SharedBuffer<u8> reply(replysize);
4065 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4066 writeS16(&reply[2], p.X);
4067 writeS16(&reply[4], p.Y);
4068 writeS16(&reply[6], p.Z);
4069 memcpy(&reply[8], *blockdata, blockdata.getSize());
4071 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4072 <<": \tpacket size: "<<replysize<<std::endl;*/
4077 m_con.Send(peer_id, 1, reply, true);
4080 void Server::SendBlocks(float dtime)
4082 DSTACK(__FUNCTION_NAME);
4084 JMutexAutoLock envlock(m_env_mutex);
4085 JMutexAutoLock conlock(m_con_mutex);
4087 //TimeTaker timer("Server::SendBlocks");
4089 core::array<PrioritySortedBlockTransfer> queue;
4091 s32 total_sending = 0;
4094 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
4096 for(core::map<u16, RemoteClient*>::Iterator
4097 i = m_clients.getIterator();
4098 i.atEnd() == false; i++)
4100 RemoteClient *client = i.getNode()->getValue();
4101 assert(client->peer_id == i.getNode()->getKey());
4103 total_sending += client->SendingCount();
4105 if(client->serialization_version == SER_FMT_VER_INVALID)
4108 client->GetNextBlocks(this, dtime, queue);
4113 // Lowest priority number comes first.
4114 // Lowest is most important.
4117 for(u32 i=0; i<queue.size(); i++)
4119 //TODO: Calculate limit dynamically
4120 if(total_sending >= g_settings.getS32
4121 ("max_simultaneous_block_sends_server_total"))
4124 PrioritySortedBlockTransfer q = queue[i];
4126 MapBlock *block = NULL;
4129 block = m_env.getMap().getBlockNoCreate(q.pos);
4131 catch(InvalidPositionException &e)
4136 RemoteClient *client = getClient(q.peer_id);
4138 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4140 client->SentBlock(q.pos);
4150 void Server::UpdateCrafting(u16 peer_id)
4152 DSTACK(__FUNCTION_NAME);
4154 Player* player = m_env.getPlayer(peer_id);
4158 Calculate crafting stuff
4160 if(g_settings.getBool("creative_mode") == false)
4162 InventoryList *clist = player->inventory.getList("craft");
4163 InventoryList *rlist = player->inventory.getList("craftresult");
4165 if(rlist && rlist->getUsedSlots() == 0)
4166 player->craftresult_is_preview = true;
4168 if(rlist && player->craftresult_is_preview)
4170 rlist->clearItems();
4172 if(clist && rlist && player->craftresult_is_preview)
4174 InventoryItem *items[9];
4175 for(u16 i=0; i<9; i++)
4177 items[i] = clist->getItem(i);
4180 // Get result of crafting grid
4181 InventoryItem *result = craft_get_result(items);
4183 rlist->addItem(result);
4186 } // if creative_mode == false
4189 RemoteClient* Server::getClient(u16 peer_id)
4191 DSTACK(__FUNCTION_NAME);
4192 //JMutexAutoLock lock(m_con_mutex);
4193 core::map<u16, RemoteClient*>::Node *n;
4194 n = m_clients.find(peer_id);
4195 // A client should exist for all peers
4197 return n->getValue();
4200 std::wstring Server::getStatusString()
4202 std::wostringstream os(std::ios_base::binary);
4205 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4207 os<<L", uptime="<<m_uptime.get();
4208 // Information about clients
4210 for(core::map<u16, RemoteClient*>::Iterator
4211 i = m_clients.getIterator();
4212 i.atEnd() == false; i++)
4214 // Get client and check that it is valid
4215 RemoteClient *client = i.getNode()->getValue();
4216 assert(client->peer_id == i.getNode()->getKey());
4217 if(client->serialization_version == SER_FMT_VER_INVALID)
4220 Player *player = m_env.getPlayer(client->peer_id);
4221 // Get name of player
4222 std::wstring name = L"unknown";
4224 name = narrow_to_wide(player->getName());
4225 // Add name to information string
4229 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4230 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4231 if(g_settings.get("motd") != "")
4232 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4236 v3f findSpawnPos(ServerMap &map)
4238 //return v3f(50,50,50)*BS;
4241 s16 groundheight = 0;
4244 nodepos = v2s16(0,0);
4249 // Try to find a good place a few times
4250 for(s32 i=0; i<1000; i++)
4253 // We're going to try to throw the player to this position
4254 nodepos = v2s16(-range + (myrand()%(range*2)),
4255 -range + (myrand()%(range*2)));
4256 v2s16 sectorpos = getNodeSectorPos(nodepos);
4257 // Get sector (NOTE: Don't get because it's slow)
4258 //m_env.getMap().emergeSector(sectorpos);
4259 // Get ground height at point (fallbacks to heightmap function)
4260 groundheight = map.findGroundLevel(nodepos);
4261 // Don't go underwater
4262 if(groundheight < WATER_LEVEL)
4264 //dstream<<"-> Underwater"<<std::endl;
4267 // Don't go to high places
4268 if(groundheight > WATER_LEVEL + 4)
4270 //dstream<<"-> Underwater"<<std::endl;
4274 // Found a good place
4275 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4280 // If no suitable place was not found, go above water at least.
4281 if(groundheight < WATER_LEVEL)
4282 groundheight = WATER_LEVEL;
4284 return intToFloat(v3s16(
4291 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4294 Try to get an existing player
4296 Player *player = m_env.getPlayer(name);
4299 // If player is already connected, cancel
4300 if(player->peer_id != 0)
4302 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4307 player->peer_id = peer_id;
4309 // Reset inventory to creative if in creative mode
4310 if(g_settings.getBool("creative_mode"))
4312 // Warning: double code below
4313 // Backup actual inventory
4314 player->inventory_backup = new Inventory();
4315 *(player->inventory_backup) = player->inventory;
4316 // Set creative inventory
4317 craft_set_creative_inventory(player);
4324 If player with the wanted peer_id already exists, cancel.
4326 if(m_env.getPlayer(peer_id) != NULL)
4328 dstream<<"emergePlayer(): Player with wrong name but same"
4329 " peer_id already exists"<<std::endl;
4337 player = new ServerRemotePlayer();
4338 //player->peer_id = c.peer_id;
4339 //player->peer_id = PEER_ID_INEXISTENT;
4340 player->peer_id = peer_id;
4341 player->updateName(name);
4342 m_authmanager.add(name);
4343 m_authmanager.setPassword(name, password);
4344 m_authmanager.setPrivs(name,
4345 stringToPrivs(g_settings.get("default_privs")));
4351 dstream<<"Server: Finding spawn place for player \""
4352 <<player->getName()<<"\""<<std::endl;
4354 v3f pos = findSpawnPos(m_env.getServerMap());
4356 player->setPosition(pos);
4359 Add player to environment
4362 m_env.addPlayer(player);
4365 Add stuff to inventory
4368 if(g_settings.getBool("creative_mode"))
4370 // Warning: double code above
4371 // Backup actual inventory
4372 player->inventory_backup = new Inventory();
4373 *(player->inventory_backup) = player->inventory;
4374 // Set creative inventory
4375 craft_set_creative_inventory(player);
4377 else if(g_settings.getBool("give_initial_stuff"))
4379 craft_give_initial_stuff(player);
4384 } // create new player
4387 void Server::handlePeerChange(PeerChange &c)
4389 JMutexAutoLock envlock(m_env_mutex);
4390 JMutexAutoLock conlock(m_con_mutex);
4392 if(c.type == PEER_ADDED)
4399 core::map<u16, RemoteClient*>::Node *n;
4400 n = m_clients.find(c.peer_id);
4401 // The client shouldn't already exist
4405 RemoteClient *client = new RemoteClient();
4406 client->peer_id = c.peer_id;
4407 m_clients.insert(client->peer_id, client);
4410 else if(c.type == PEER_REMOVED)
4417 core::map<u16, RemoteClient*>::Node *n;
4418 n = m_clients.find(c.peer_id);
4419 // The client should exist
4423 Mark objects to be not known by the client
4425 RemoteClient *client = n->getValue();
4427 for(core::map<u16, bool>::Iterator
4428 i = client->m_known_objects.getIterator();
4429 i.atEnd()==false; i++)
4432 u16 id = i.getNode()->getKey();
4433 ServerActiveObject* obj = m_env.getActiveObject(id);
4435 if(obj && obj->m_known_by_count > 0)
4436 obj->m_known_by_count--;
4439 // Collect information about leaving in chat
4440 std::wstring message;
4442 Player *player = m_env.getPlayer(c.peer_id);
4445 std::wstring name = narrow_to_wide(player->getName());
4448 message += L" left game";
4450 message += L" (timed out)";
4456 m_env.removePlayer(c.peer_id);
4459 // Set player client disconnected
4461 Player *player = m_env.getPlayer(c.peer_id);
4463 player->peer_id = 0;
4467 delete m_clients[c.peer_id];
4468 m_clients.remove(c.peer_id);
4470 // Send player info to all remaining clients
4473 // Send leave chat message to all remaining clients
4474 BroadcastChatMessage(message);
4483 void Server::handlePeerChanges()
4485 while(m_peer_change_queue.size() > 0)
4487 PeerChange c = m_peer_change_queue.pop_front();
4489 dout_server<<"Server: Handling peer change: "
4490 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4493 handlePeerChange(c);
4497 u64 Server::getPlayerPrivs(Player *player)
4501 std::string playername = player->getName();
4502 // Local player gets all privileges regardless of
4503 // what's set on their account.
4504 if(g_settings.get("name") == playername)
4510 return getPlayerAuthPrivs(playername);
4514 void dedicated_server_loop(Server &server, bool &kill)
4516 DSTACK(__FUNCTION_NAME);
4518 dstream<<DTIME<<std::endl;
4519 dstream<<"========================"<<std::endl;
4520 dstream<<"Running dedicated server"<<std::endl;
4521 dstream<<"========================"<<std::endl;
4524 IntervalLimiter m_profiler_interval;
4528 // This is kind of a hack but can be done like this
4529 // because server.step() is very light
4531 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4536 if(server.getShutdownRequested() || kill)
4538 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4545 float profiler_print_interval =
4546 g_settings.getFloat("profiler_print_interval");
4547 if(profiler_print_interval != 0)
4549 if(m_profiler_interval.step(0.030, profiler_print_interval))
4551 dstream<<"Profiler:"<<std::endl;
4552 g_profiler.print(dstream);
4560 static int counter = 0;
4566 core::list<PlayerInfo> list = server.getPlayerInfo();
4567 core::list<PlayerInfo>::Iterator i;
4568 static u32 sum_old = 0;
4569 u32 sum = PIChecksum(list);
4572 dstream<<DTIME<<"Player info:"<<std::endl;
4573 for(i=list.begin(); i!=list.end(); i++)
4575 i->PrintLine(&dstream);