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);
2754 if(mineral != MINERAL_NONE)
2755 item = getDiggedMineralItem(mineral);
2760 std::string &extra_dug_s = content_features(material).extra_dug_item;
2761 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2762 if(extra_dug_s != "" && extra_rarity != 0
2763 && myrand() % extra_rarity == 0)
2765 std::istringstream is(extra_dug_s, std::ios::binary);
2766 item = InventoryItem::deSerialize(is);
2772 // Add a item to inventory
2773 player->inventory.addItem("main", item);
2776 UpdateCrafting(player->peer_id);
2777 SendInventory(player->peer_id);
2783 (this takes some time so it is done after the quick stuff)
2786 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2788 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2791 Set blocks not sent to far players
2793 for(core::list<u16>::Iterator
2794 i = far_players.begin();
2795 i != far_players.end(); i++)
2798 RemoteClient *client = getClient(peer_id);
2801 client->SetBlocksNotSent(modified_blocks);
2808 else if(action == 1)
2811 InventoryList *ilist = player->inventory.getList("main");
2816 InventoryItem *item = ilist->getItem(item_i);
2818 // If there is no item, it is not possible to add it anywhere
2823 Handle material items
2825 if(std::string("MaterialItem") == item->getName())
2828 // Don't add a node if this is not a free space
2829 MapNode n2 = m_env.getMap().getNode(p_over);
2830 bool no_enough_privs =
2831 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2833 dstream<<"Player "<<player->getName()<<" cannot add node"
2834 <<" because privileges are "<<getPlayerPrivs(player)
2837 if(content_features(n2).buildable_to == false
2840 // Client probably has wrong data.
2841 // Set block not sent, so that client will get
2843 dstream<<"Client "<<peer_id<<" tried to place"
2844 <<" node in invalid position; setting"
2845 <<" MapBlock not sent."<<std::endl;
2846 RemoteClient *client = getClient(peer_id);
2847 v3s16 blockpos = getNodeBlockPos(p_over);
2848 client->SetBlockNotSent(blockpos);
2852 catch(InvalidPositionException &e)
2854 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2855 <<" Adding block to emerge queue."
2857 m_emerge_queue.addBlock(peer_id,
2858 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2862 // Reset build time counter
2863 getClient(peer->id)->m_time_from_building = 0.0;
2866 MaterialItem *mitem = (MaterialItem*)item;
2868 n.setContent(mitem->getMaterial());
2870 // Calculate direction for wall mounted stuff
2871 if(content_features(n).wall_mounted)
2872 n.param2 = packDir(p_under - p_over);
2874 // Calculate the direction for furnaces and chests and stuff
2875 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2877 v3f playerpos = player->getPosition();
2878 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2879 blockpos = blockpos.normalize();
2881 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2895 Send to all close-by players
2897 core::list<u16> far_players;
2898 sendAddNode(p_over, n, 0, &far_players, 30);
2903 InventoryList *ilist = player->inventory.getList("main");
2904 if(g_settings.getBool("creative_mode") == false && ilist)
2906 // Remove from inventory and send inventory
2907 if(mitem->getCount() == 1)
2908 ilist->deleteItem(item_i);
2912 UpdateCrafting(peer_id);
2913 SendInventory(peer_id);
2919 This takes some time so it is done after the quick stuff
2921 core::map<v3s16, MapBlock*> modified_blocks;
2923 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2925 std::string p_name = std::string(player->getName());
2926 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2929 Set blocks not sent to far players
2931 for(core::list<u16>::Iterator
2932 i = far_players.begin();
2933 i != far_players.end(); i++)
2936 RemoteClient *client = getClient(peer_id);
2939 client->SetBlocksNotSent(modified_blocks);
2943 Calculate special events
2946 /*if(n.d == CONTENT_MESE)
2949 for(s16 z=-1; z<=1; z++)
2950 for(s16 y=-1; y<=1; y++)
2951 for(s16 x=-1; x<=1; x++)
2958 Place other item (not a block)
2962 v3s16 blockpos = getNodeBlockPos(p_over);
2965 Check that the block is loaded so that the item
2966 can properly be added to the static list too
2968 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2971 derr_server<<"Error while placing object: "
2972 "block not found"<<std::endl;
2977 If in creative mode, item dropping is disabled unless
2978 player has build privileges
2980 if(g_settings.getBool("creative_mode") &&
2981 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2983 derr_server<<"Not allowing player to drop item: "
2984 "creative mode and no build privs"<<std::endl;
2988 dout_server<<"Placing a miscellaneous item on map"
2991 // Calculate a position for it
2992 v3f pos = intToFloat(p_over, BS);
2994 pos.Y -= BS*0.25; // let it drop a bit
2996 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2997 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3002 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
3006 derr_server<<"WARNING: item resulted in NULL object, "
3007 <<"not placing onto map"
3012 // Add the object to the environment
3013 m_env.addActiveObject(obj);
3015 dout_server<<"Placed object"<<std::endl;
3017 if(g_settings.getBool("creative_mode") == false)
3019 // Delete the right amount of items from the slot
3020 u16 dropcount = item->getDropCount();
3022 // Delete item if all gone
3023 if(item->getCount() <= dropcount)
3025 if(item->getCount() < dropcount)
3026 dstream<<"WARNING: Server: dropped more items"
3027 <<" than the slot contains"<<std::endl;
3029 InventoryList *ilist = player->inventory.getList("main");
3031 // Remove from inventory and send inventory
3032 ilist->deleteItem(item_i);
3034 // Else decrement it
3036 item->remove(dropcount);
3039 UpdateCrafting(peer_id);
3040 SendInventory(peer_id);
3048 Catch invalid actions
3052 derr_server<<"WARNING: Server: Invalid action "
3053 <<action<<std::endl;
3057 else if(command == TOSERVER_RELEASE)
3066 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
3069 else if(command == TOSERVER_SIGNTEXT)
3071 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3080 std::string datastring((char*)&data[2], datasize-2);
3081 std::istringstream is(datastring, std::ios_base::binary);
3084 is.read((char*)buf, 6);
3085 v3s16 blockpos = readV3S16(buf);
3086 is.read((char*)buf, 2);
3087 s16 id = readS16(buf);
3088 is.read((char*)buf, 2);
3089 u16 textlen = readU16(buf);
3091 for(u16 i=0; i<textlen; i++)
3093 is.read((char*)buf, 1);
3094 text += (char)buf[0];
3097 MapBlock *block = NULL;
3100 block = m_env.getMap().getBlockNoCreate(blockpos);
3102 catch(InvalidPositionException &e)
3104 derr_server<<"Error while setting sign text: "
3105 "block not found"<<std::endl;
3109 MapBlockObject *obj = block->getObject(id);
3112 derr_server<<"Error while setting sign text: "
3113 "object not found"<<std::endl;
3117 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
3119 derr_server<<"Error while setting sign text: "
3120 "object is not a sign"<<std::endl;
3124 ((SignObject*)obj)->setText(text);
3126 obj->getBlock()->setChangedFlag();
3128 else if(command == TOSERVER_SIGNNODETEXT)
3130 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3138 std::string datastring((char*)&data[2], datasize-2);
3139 std::istringstream is(datastring, std::ios_base::binary);
3142 is.read((char*)buf, 6);
3143 v3s16 p = readV3S16(buf);
3144 is.read((char*)buf, 2);
3145 u16 textlen = readU16(buf);
3147 for(u16 i=0; i<textlen; i++)
3149 is.read((char*)buf, 1);
3150 text += (char)buf[0];
3153 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3156 if(meta->typeId() != CONTENT_SIGN_WALL)
3158 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3159 signmeta->setText(text);
3161 v3s16 blockpos = getNodeBlockPos(p);
3162 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
3165 block->setChangedFlag();
3168 for(core::map<u16, RemoteClient*>::Iterator
3169 i = m_clients.getIterator();
3170 i.atEnd()==false; i++)
3172 RemoteClient *client = i.getNode()->getValue();
3173 client->SetBlockNotSent(blockpos);
3176 else if(command == TOSERVER_INVENTORY_ACTION)
3178 /*// Ignore inventory changes if in creative mode
3179 if(g_settings.getBool("creative_mode") == true)
3181 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3185 // Strip command and create a stream
3186 std::string datastring((char*)&data[2], datasize-2);
3187 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3188 std::istringstream is(datastring, std::ios_base::binary);
3190 InventoryAction *a = InventoryAction::deSerialize(is);
3195 c.current_player = player;
3198 Handle craftresult specially if not in creative mode
3200 bool disable_action = false;
3201 if(a->getType() == IACTION_MOVE
3202 && g_settings.getBool("creative_mode") == false)
3204 IMoveAction *ma = (IMoveAction*)a;
3205 if(ma->to_inv == "current_player" &&
3206 ma->from_inv == "current_player")
3208 InventoryList *rlist = player->inventory.getList("craftresult");
3210 InventoryList *clist = player->inventory.getList("craft");
3212 InventoryList *mlist = player->inventory.getList("main");
3215 Craftresult is no longer preview if something
3218 if(ma->to_list == "craftresult"
3219 && ma->from_list != "craftresult")
3221 // If it currently is a preview, remove
3223 if(player->craftresult_is_preview)
3225 rlist->deleteItem(0);
3227 player->craftresult_is_preview = false;
3230 Crafting takes place if this condition is true.
3232 if(player->craftresult_is_preview &&
3233 ma->from_list == "craftresult")
3235 player->craftresult_is_preview = false;
3236 clist->decrementMaterials(1);
3239 If the craftresult is placed on itself, move it to
3240 main inventory instead of doing the action
3242 if(ma->to_list == "craftresult"
3243 && ma->from_list == "craftresult")
3245 disable_action = true;
3247 InventoryItem *item1 = rlist->changeItem(0, NULL);
3248 mlist->addItem(item1);
3251 // Disallow moving items if not allowed to build
3252 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3256 // if it's a locking chest, only allow the owner or server admins to move items
3257 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3259 Strfnd fn(ma->from_inv);
3260 std::string id0 = fn.next(":");
3261 if(id0 == "nodemeta")
3264 p.X = stoi(fn.next(","));
3265 p.Y = stoi(fn.next(","));
3266 p.Z = stoi(fn.next(","));
3267 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3268 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3269 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3270 if (lcm->getOwner() != player->getName())
3275 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3277 Strfnd fn(ma->to_inv);
3278 std::string id0 = fn.next(":");
3279 if(id0 == "nodemeta")
3282 p.X = stoi(fn.next(","));
3283 p.Y = stoi(fn.next(","));
3284 p.Z = stoi(fn.next(","));
3285 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3286 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3287 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3288 if (lcm->getOwner() != player->getName())
3295 if(disable_action == false)
3297 // Feed action to player inventory
3305 UpdateCrafting(player->peer_id);
3306 SendInventory(player->peer_id);
3311 dstream<<"TOSERVER_INVENTORY_ACTION: "
3312 <<"InventoryAction::deSerialize() returned NULL"
3316 else if(command == TOSERVER_CHAT_MESSAGE)
3324 std::string datastring((char*)&data[2], datasize-2);
3325 std::istringstream is(datastring, std::ios_base::binary);
3328 is.read((char*)buf, 2);
3329 u16 len = readU16(buf);
3331 std::wstring message;
3332 for(u16 i=0; i<len; i++)
3334 is.read((char*)buf, 2);
3335 message += (wchar_t)readU16(buf);
3338 // Get player name of this client
3339 std::wstring name = narrow_to_wide(player->getName());
3341 // Line to send to players
3343 // Whether to send to the player that sent the line
3344 bool send_to_sender = false;
3345 // Whether to send to other players
3346 bool send_to_others = false;
3348 // Local player gets all privileges regardless of
3349 // what's set on their account.
3350 u64 privs = getPlayerPrivs(player);
3353 if(message[0] == L'/')
3355 size_t strip_size = 1;
3356 if (message[1] == L'#') // support old-style commans
3358 message = message.substr(strip_size);
3360 WStrfnd f1(message);
3361 f1.next(L" "); // Skip over /#whatever
3362 std::wstring paramstring = f1.next(L"");
3364 ServerCommandContext *ctx = new ServerCommandContext(
3365 str_split(message, L' '),
3372 std::wstring reply(processServerCommand(ctx));
3373 send_to_sender = ctx->flags & SEND_TO_SENDER;
3374 send_to_others = ctx->flags & SEND_TO_OTHERS;
3376 if (ctx->flags & SEND_NO_PREFIX)
3379 line += L"Server: " + reply;
3386 if(privs & PRIV_SHOUT)
3392 send_to_others = true;
3396 line += L"Server: You are not allowed to shout";
3397 send_to_sender = true;
3403 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3406 Send the message to clients
3408 for(core::map<u16, RemoteClient*>::Iterator
3409 i = m_clients.getIterator();
3410 i.atEnd() == false; i++)
3412 // Get client and check that it is valid
3413 RemoteClient *client = i.getNode()->getValue();
3414 assert(client->peer_id == i.getNode()->getKey());
3415 if(client->serialization_version == SER_FMT_VER_INVALID)
3419 bool sender_selected = (peer_id == client->peer_id);
3420 if(sender_selected == true && send_to_sender == false)
3422 if(sender_selected == false && send_to_others == false)
3425 SendChatMessage(client->peer_id, line);
3429 else if(command == TOSERVER_DAMAGE)
3431 if(g_settings.getBool("enable_damage"))
3433 std::string datastring((char*)&data[2], datasize-2);
3434 std::istringstream is(datastring, std::ios_base::binary);
3435 u8 damage = readU8(is);
3436 if(player->hp > damage)
3438 player->hp -= damage;
3444 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3447 v3f pos = findSpawnPos(m_env.getServerMap());
3448 player->setPosition(pos);
3450 SendMovePlayer(player);
3451 SendPlayerHP(player);
3453 //TODO: Throw items around
3457 SendPlayerHP(player);
3459 else if(command == TOSERVER_PASSWORD)
3462 [0] u16 TOSERVER_PASSWORD
3463 [2] u8[28] old password
3464 [30] u8[28] new password
3467 if(datasize != 2+PASSWORD_SIZE*2)
3469 /*char password[PASSWORD_SIZE];
3470 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3471 password[i] = data[2+i];
3472 password[PASSWORD_SIZE-1] = 0;*/
3474 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3482 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3484 char c = data[2+PASSWORD_SIZE+i];
3490 dstream<<"Server: Client requests a password change from "
3491 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3493 std::string playername = player->getName();
3495 if(m_authmanager.exists(playername) == false)
3497 dstream<<"Server: playername not found in authmanager"<<std::endl;
3498 // Wrong old password supplied!!
3499 SendChatMessage(peer_id, L"playername not found in authmanager");
3503 std::string checkpwd = m_authmanager.getPassword(playername);
3505 if(oldpwd != checkpwd)
3507 dstream<<"Server: invalid old password"<<std::endl;
3508 // Wrong old password supplied!!
3509 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3513 m_authmanager.setPassword(playername, newpwd);
3515 dstream<<"Server: password change successful for "<<playername
3517 SendChatMessage(peer_id, L"Password change successful");
3519 else if (command == TOSERVER_PLAYERITEM)
3524 u16 item = readU16(&data[2]);
3525 player->wieldItem(item);
3526 SendWieldedItem(player);
3530 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3531 "unknown command "<<command<<std::endl;
3535 catch(SendFailedException &e)
3537 derr_server<<"Server::ProcessData(): SendFailedException: "
3543 void Server::onMapEditEvent(MapEditEvent *event)
3545 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3546 if(m_ignore_map_edit_events)
3548 MapEditEvent *e = event->clone();
3549 m_unsent_map_edit_queue.push_back(e);
3552 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3554 if(id == "current_player")
3556 assert(c->current_player);
3557 return &(c->current_player->inventory);
3561 std::string id0 = fn.next(":");
3563 if(id0 == "nodemeta")
3566 p.X = stoi(fn.next(","));
3567 p.Y = stoi(fn.next(","));
3568 p.Z = stoi(fn.next(","));
3569 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3571 return meta->getInventory();
3572 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3573 <<"no metadata found"<<std::endl;
3577 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3580 void Server::inventoryModified(InventoryContext *c, std::string id)
3582 if(id == "current_player")
3584 assert(c->current_player);
3586 UpdateCrafting(c->current_player->peer_id);
3587 SendInventory(c->current_player->peer_id);
3592 std::string id0 = fn.next(":");
3594 if(id0 == "nodemeta")
3597 p.X = stoi(fn.next(","));
3598 p.Y = stoi(fn.next(","));
3599 p.Z = stoi(fn.next(","));
3600 v3s16 blockpos = getNodeBlockPos(p);
3602 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3604 meta->inventoryModified();
3606 for(core::map<u16, RemoteClient*>::Iterator
3607 i = m_clients.getIterator();
3608 i.atEnd()==false; i++)
3610 RemoteClient *client = i.getNode()->getValue();
3611 client->SetBlockNotSent(blockpos);
3617 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3620 core::list<PlayerInfo> Server::getPlayerInfo()
3622 DSTACK(__FUNCTION_NAME);
3623 JMutexAutoLock envlock(m_env_mutex);
3624 JMutexAutoLock conlock(m_con_mutex);
3626 core::list<PlayerInfo> list;
3628 core::list<Player*> players = m_env.getPlayers();
3630 core::list<Player*>::Iterator i;
3631 for(i = players.begin();
3632 i != players.end(); i++)
3636 Player *player = *i;
3639 con::Peer *peer = m_con.GetPeer(player->peer_id);
3640 // Copy info from peer to info struct
3642 info.address = peer->address;
3643 info.avg_rtt = peer->avg_rtt;
3645 catch(con::PeerNotFoundException &e)
3647 // Set dummy peer info
3649 info.address = Address(0,0,0,0,0);
3653 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3654 info.position = player->getPosition();
3656 list.push_back(info);
3663 void Server::peerAdded(con::Peer *peer)
3665 DSTACK(__FUNCTION_NAME);
3666 dout_server<<"Server::peerAdded(): peer->id="
3667 <<peer->id<<std::endl;
3670 c.type = PEER_ADDED;
3671 c.peer_id = peer->id;
3673 m_peer_change_queue.push_back(c);
3676 void Server::deletingPeer(con::Peer *peer, bool timeout)
3678 DSTACK(__FUNCTION_NAME);
3679 dout_server<<"Server::deletingPeer(): peer->id="
3680 <<peer->id<<", timeout="<<timeout<<std::endl;
3683 c.type = PEER_REMOVED;
3684 c.peer_id = peer->id;
3685 c.timeout = timeout;
3686 m_peer_change_queue.push_back(c);
3693 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3695 DSTACK(__FUNCTION_NAME);
3696 std::ostringstream os(std::ios_base::binary);
3698 writeU16(os, TOCLIENT_HP);
3702 std::string s = os.str();
3703 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3705 con.Send(peer_id, 0, data, true);
3708 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3709 const std::wstring &reason)
3711 DSTACK(__FUNCTION_NAME);
3712 std::ostringstream os(std::ios_base::binary);
3714 writeU16(os, TOCLIENT_ACCESS_DENIED);
3715 os<<serializeWideString(reason);
3718 std::string s = os.str();
3719 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3721 con.Send(peer_id, 0, data, true);
3725 Non-static send methods
3728 void Server::SendObjectData(float dtime)
3730 DSTACK(__FUNCTION_NAME);
3732 core::map<v3s16, bool> stepped_blocks;
3734 for(core::map<u16, RemoteClient*>::Iterator
3735 i = m_clients.getIterator();
3736 i.atEnd() == false; i++)
3738 u16 peer_id = i.getNode()->getKey();
3739 RemoteClient *client = i.getNode()->getValue();
3740 assert(client->peer_id == peer_id);
3742 if(client->serialization_version == SER_FMT_VER_INVALID)
3745 client->SendObjectData(this, dtime, stepped_blocks);
3749 void Server::SendPlayerInfos()
3751 DSTACK(__FUNCTION_NAME);
3753 //JMutexAutoLock envlock(m_env_mutex);
3755 // Get connected players
3756 core::list<Player*> players = m_env.getPlayers(true);
3758 u32 player_count = players.getSize();
3759 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3761 SharedBuffer<u8> data(datasize);
3762 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3765 core::list<Player*>::Iterator i;
3766 for(i = players.begin();
3767 i != players.end(); i++)
3769 Player *player = *i;
3771 /*dstream<<"Server sending player info for player with "
3772 "peer_id="<<player->peer_id<<std::endl;*/
3774 writeU16(&data[start], player->peer_id);
3775 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3776 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3777 start += 2+PLAYERNAME_SIZE;
3780 //JMutexAutoLock conlock(m_con_mutex);
3783 m_con.SendToAll(0, data, true);
3786 void Server::SendInventory(u16 peer_id)
3788 DSTACK(__FUNCTION_NAME);
3790 Player* player = m_env.getPlayer(peer_id);
3797 std::ostringstream os;
3798 //os.imbue(std::locale("C"));
3800 player->inventory.serialize(os);
3802 std::string s = os.str();
3804 SharedBuffer<u8> data(s.size()+2);
3805 writeU16(&data[0], TOCLIENT_INVENTORY);
3806 memcpy(&data[2], s.c_str(), s.size());
3809 m_con.Send(peer_id, 0, data, true);
3812 std::string getWieldedItemString(const Player *player)
3814 const InventoryItem *item = player->getWieldItem();
3816 return std::string("");
3817 std::ostringstream os(std::ios_base::binary);
3818 item->serialize(os);
3822 void Server::SendWieldedItem(const Player* player)
3824 DSTACK(__FUNCTION_NAME);
3828 std::ostringstream os(std::ios_base::binary);
3830 writeU16(os, TOCLIENT_PLAYERITEM);
3832 writeU16(os, player->peer_id);
3833 os<<serializeString(getWieldedItemString(player));
3836 std::string s = os.str();
3837 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3839 m_con.SendToAll(0, data, true);
3842 void Server::SendPlayerItems()
3844 DSTACK(__FUNCTION_NAME);
3846 std::ostringstream os(std::ios_base::binary);
3847 core::list<Player *> players = m_env.getPlayers(true);
3849 writeU16(os, TOCLIENT_PLAYERITEM);
3850 writeU16(os, players.size());
3851 core::list<Player *>::Iterator i;
3852 for(i = players.begin(); i != players.end(); ++i)
3855 writeU16(os, p->peer_id);
3856 os<<serializeString(getWieldedItemString(p));
3860 std::string s = os.str();
3861 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3863 m_con.SendToAll(0, data, true);
3866 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3868 DSTACK(__FUNCTION_NAME);
3870 std::ostringstream os(std::ios_base::binary);
3874 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3875 os.write((char*)buf, 2);
3878 writeU16(buf, message.size());
3879 os.write((char*)buf, 2);
3882 for(u32 i=0; i<message.size(); i++)
3886 os.write((char*)buf, 2);
3890 std::string s = os.str();
3891 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3893 m_con.Send(peer_id, 0, data, true);
3896 void Server::BroadcastChatMessage(const std::wstring &message)
3898 for(core::map<u16, RemoteClient*>::Iterator
3899 i = m_clients.getIterator();
3900 i.atEnd() == false; i++)
3902 // Get client and check that it is valid
3903 RemoteClient *client = i.getNode()->getValue();
3904 assert(client->peer_id == i.getNode()->getKey());
3905 if(client->serialization_version == SER_FMT_VER_INVALID)
3908 SendChatMessage(client->peer_id, message);
3912 void Server::SendPlayerHP(Player *player)
3914 SendHP(m_con, player->peer_id, player->hp);
3917 void Server::SendMovePlayer(Player *player)
3919 DSTACK(__FUNCTION_NAME);
3920 std::ostringstream os(std::ios_base::binary);
3922 writeU16(os, TOCLIENT_MOVE_PLAYER);
3923 writeV3F1000(os, player->getPosition());
3924 writeF1000(os, player->getPitch());
3925 writeF1000(os, player->getYaw());
3928 v3f pos = player->getPosition();
3929 f32 pitch = player->getPitch();
3930 f32 yaw = player->getYaw();
3931 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3932 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3939 std::string s = os.str();
3940 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3942 m_con.Send(player->peer_id, 0, data, true);
3945 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3946 core::list<u16> *far_players, float far_d_nodes)
3948 float maxd = far_d_nodes*BS;
3949 v3f p_f = intToFloat(p, BS);
3953 SharedBuffer<u8> reply(replysize);
3954 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3955 writeS16(&reply[2], p.X);
3956 writeS16(&reply[4], p.Y);
3957 writeS16(&reply[6], p.Z);
3959 for(core::map<u16, RemoteClient*>::Iterator
3960 i = m_clients.getIterator();
3961 i.atEnd() == false; i++)
3963 // Get client and check that it is valid
3964 RemoteClient *client = i.getNode()->getValue();
3965 assert(client->peer_id == i.getNode()->getKey());
3966 if(client->serialization_version == SER_FMT_VER_INVALID)
3969 // Don't send if it's the same one
3970 if(client->peer_id == ignore_id)
3976 Player *player = m_env.getPlayer(client->peer_id);
3979 // If player is far away, only set modified blocks not sent
3980 v3f player_pos = player->getPosition();
3981 if(player_pos.getDistanceFrom(p_f) > maxd)
3983 far_players->push_back(client->peer_id);
3990 m_con.Send(client->peer_id, 0, reply, true);
3994 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3995 core::list<u16> *far_players, float far_d_nodes)
3997 float maxd = far_d_nodes*BS;
3998 v3f p_f = intToFloat(p, BS);
4000 for(core::map<u16, RemoteClient*>::Iterator
4001 i = m_clients.getIterator();
4002 i.atEnd() == false; i++)
4004 // Get client and check that it is valid
4005 RemoteClient *client = i.getNode()->getValue();
4006 assert(client->peer_id == i.getNode()->getKey());
4007 if(client->serialization_version == SER_FMT_VER_INVALID)
4010 // Don't send if it's the same one
4011 if(client->peer_id == ignore_id)
4017 Player *player = m_env.getPlayer(client->peer_id);
4020 // If player is far away, only set modified blocks not sent
4021 v3f player_pos = player->getPosition();
4022 if(player_pos.getDistanceFrom(p_f) > maxd)
4024 far_players->push_back(client->peer_id);
4031 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4032 SharedBuffer<u8> reply(replysize);
4033 writeU16(&reply[0], TOCLIENT_ADDNODE);
4034 writeS16(&reply[2], p.X);
4035 writeS16(&reply[4], p.Y);
4036 writeS16(&reply[6], p.Z);
4037 n.serialize(&reply[8], client->serialization_version);
4040 m_con.Send(client->peer_id, 0, reply, true);
4044 void Server::setBlockNotSent(v3s16 p)
4046 for(core::map<u16, RemoteClient*>::Iterator
4047 i = m_clients.getIterator();
4048 i.atEnd()==false; i++)
4050 RemoteClient *client = i.getNode()->getValue();
4051 client->SetBlockNotSent(p);
4055 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4057 DSTACK(__FUNCTION_NAME);
4059 v3s16 p = block->getPos();
4063 bool completely_air = true;
4064 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4065 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4066 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4068 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4070 completely_air = false;
4071 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4076 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4078 dstream<<"[completely air] ";
4083 Create a packet with the block in the right format
4086 std::ostringstream os(std::ios_base::binary);
4087 block->serialize(os, ver);
4088 std::string s = os.str();
4089 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4091 u32 replysize = 8 + blockdata.getSize();
4092 SharedBuffer<u8> reply(replysize);
4093 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4094 writeS16(&reply[2], p.X);
4095 writeS16(&reply[4], p.Y);
4096 writeS16(&reply[6], p.Z);
4097 memcpy(&reply[8], *blockdata, blockdata.getSize());
4099 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4100 <<": \tpacket size: "<<replysize<<std::endl;*/
4105 m_con.Send(peer_id, 1, reply, true);
4108 void Server::SendBlocks(float dtime)
4110 DSTACK(__FUNCTION_NAME);
4112 JMutexAutoLock envlock(m_env_mutex);
4113 JMutexAutoLock conlock(m_con_mutex);
4115 //TimeTaker timer("Server::SendBlocks");
4117 core::array<PrioritySortedBlockTransfer> queue;
4119 s32 total_sending = 0;
4122 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
4124 for(core::map<u16, RemoteClient*>::Iterator
4125 i = m_clients.getIterator();
4126 i.atEnd() == false; i++)
4128 RemoteClient *client = i.getNode()->getValue();
4129 assert(client->peer_id == i.getNode()->getKey());
4131 total_sending += client->SendingCount();
4133 if(client->serialization_version == SER_FMT_VER_INVALID)
4136 client->GetNextBlocks(this, dtime, queue);
4141 // Lowest priority number comes first.
4142 // Lowest is most important.
4145 for(u32 i=0; i<queue.size(); i++)
4147 //TODO: Calculate limit dynamically
4148 if(total_sending >= g_settings.getS32
4149 ("max_simultaneous_block_sends_server_total"))
4152 PrioritySortedBlockTransfer q = queue[i];
4154 MapBlock *block = NULL;
4157 block = m_env.getMap().getBlockNoCreate(q.pos);
4159 catch(InvalidPositionException &e)
4164 RemoteClient *client = getClient(q.peer_id);
4166 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4168 client->SentBlock(q.pos);
4178 void Server::UpdateCrafting(u16 peer_id)
4180 DSTACK(__FUNCTION_NAME);
4182 Player* player = m_env.getPlayer(peer_id);
4186 Calculate crafting stuff
4188 if(g_settings.getBool("creative_mode") == false)
4190 InventoryList *clist = player->inventory.getList("craft");
4191 InventoryList *rlist = player->inventory.getList("craftresult");
4193 if(rlist && rlist->getUsedSlots() == 0)
4194 player->craftresult_is_preview = true;
4196 if(rlist && player->craftresult_is_preview)
4198 rlist->clearItems();
4200 if(clist && rlist && player->craftresult_is_preview)
4202 InventoryItem *items[9];
4203 for(u16 i=0; i<9; i++)
4205 items[i] = clist->getItem(i);
4208 // Get result of crafting grid
4209 InventoryItem *result = craft_get_result(items);
4211 rlist->addItem(result);
4214 } // if creative_mode == false
4217 RemoteClient* Server::getClient(u16 peer_id)
4219 DSTACK(__FUNCTION_NAME);
4220 //JMutexAutoLock lock(m_con_mutex);
4221 core::map<u16, RemoteClient*>::Node *n;
4222 n = m_clients.find(peer_id);
4223 // A client should exist for all peers
4225 return n->getValue();
4228 std::wstring Server::getStatusString()
4230 std::wostringstream os(std::ios_base::binary);
4233 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4235 os<<L", uptime="<<m_uptime.get();
4236 // Information about clients
4238 for(core::map<u16, RemoteClient*>::Iterator
4239 i = m_clients.getIterator();
4240 i.atEnd() == false; i++)
4242 // Get client and check that it is valid
4243 RemoteClient *client = i.getNode()->getValue();
4244 assert(client->peer_id == i.getNode()->getKey());
4245 if(client->serialization_version == SER_FMT_VER_INVALID)
4248 Player *player = m_env.getPlayer(client->peer_id);
4249 // Get name of player
4250 std::wstring name = L"unknown";
4252 name = narrow_to_wide(player->getName());
4253 // Add name to information string
4257 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4258 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4259 if(g_settings.get("motd") != "")
4260 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings.get("motd"));
4264 v3f findSpawnPos(ServerMap &map)
4266 //return v3f(50,50,50)*BS;
4269 s16 groundheight = 0;
4272 nodepos = v2s16(0,0);
4277 // Try to find a good place a few times
4278 for(s32 i=0; i<1000; i++)
4281 // We're going to try to throw the player to this position
4282 nodepos = v2s16(-range + (myrand()%(range*2)),
4283 -range + (myrand()%(range*2)));
4284 v2s16 sectorpos = getNodeSectorPos(nodepos);
4285 // Get sector (NOTE: Don't get because it's slow)
4286 //m_env.getMap().emergeSector(sectorpos);
4287 // Get ground height at point (fallbacks to heightmap function)
4288 groundheight = map.findGroundLevel(nodepos);
4289 // Don't go underwater
4290 if(groundheight < WATER_LEVEL)
4292 //dstream<<"-> Underwater"<<std::endl;
4295 // Don't go to high places
4296 if(groundheight > WATER_LEVEL + 4)
4298 //dstream<<"-> Underwater"<<std::endl;
4302 // Found a good place
4303 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4308 // If no suitable place was not found, go above water at least.
4309 if(groundheight < WATER_LEVEL)
4310 groundheight = WATER_LEVEL;
4312 return intToFloat(v3s16(
4319 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4322 Try to get an existing player
4324 Player *player = m_env.getPlayer(name);
4327 // If player is already connected, cancel
4328 if(player->peer_id != 0)
4330 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4335 player->peer_id = peer_id;
4337 // Reset inventory to creative if in creative mode
4338 if(g_settings.getBool("creative_mode"))
4340 // Warning: double code below
4341 // Backup actual inventory
4342 player->inventory_backup = new Inventory();
4343 *(player->inventory_backup) = player->inventory;
4344 // Set creative inventory
4345 craft_set_creative_inventory(player);
4352 If player with the wanted peer_id already exists, cancel.
4354 if(m_env.getPlayer(peer_id) != NULL)
4356 dstream<<"emergePlayer(): Player with wrong name but same"
4357 " peer_id already exists"<<std::endl;
4365 player = new ServerRemotePlayer();
4366 //player->peer_id = c.peer_id;
4367 //player->peer_id = PEER_ID_INEXISTENT;
4368 player->peer_id = peer_id;
4369 player->updateName(name);
4370 m_authmanager.add(name);
4371 m_authmanager.setPassword(name, password);
4372 m_authmanager.setPrivs(name,
4373 stringToPrivs(g_settings.get("default_privs")));
4379 dstream<<"Server: Finding spawn place for player \""
4380 <<player->getName()<<"\""<<std::endl;
4382 v3f pos = findSpawnPos(m_env.getServerMap());
4384 player->setPosition(pos);
4387 Add player to environment
4390 m_env.addPlayer(player);
4393 Add stuff to inventory
4396 if(g_settings.getBool("creative_mode"))
4398 // Warning: double code above
4399 // Backup actual inventory
4400 player->inventory_backup = new Inventory();
4401 *(player->inventory_backup) = player->inventory;
4402 // Set creative inventory
4403 craft_set_creative_inventory(player);
4405 else if(g_settings.getBool("give_initial_stuff"))
4407 craft_give_initial_stuff(player);
4412 } // create new player
4415 void Server::handlePeerChange(PeerChange &c)
4417 JMutexAutoLock envlock(m_env_mutex);
4418 JMutexAutoLock conlock(m_con_mutex);
4420 if(c.type == PEER_ADDED)
4427 core::map<u16, RemoteClient*>::Node *n;
4428 n = m_clients.find(c.peer_id);
4429 // The client shouldn't already exist
4433 RemoteClient *client = new RemoteClient();
4434 client->peer_id = c.peer_id;
4435 m_clients.insert(client->peer_id, client);
4438 else if(c.type == PEER_REMOVED)
4445 core::map<u16, RemoteClient*>::Node *n;
4446 n = m_clients.find(c.peer_id);
4447 // The client should exist
4451 Mark objects to be not known by the client
4453 RemoteClient *client = n->getValue();
4455 for(core::map<u16, bool>::Iterator
4456 i = client->m_known_objects.getIterator();
4457 i.atEnd()==false; i++)
4460 u16 id = i.getNode()->getKey();
4461 ServerActiveObject* obj = m_env.getActiveObject(id);
4463 if(obj && obj->m_known_by_count > 0)
4464 obj->m_known_by_count--;
4467 // Collect information about leaving in chat
4468 std::wstring message;
4470 Player *player = m_env.getPlayer(c.peer_id);
4473 std::wstring name = narrow_to_wide(player->getName());
4476 message += L" left game";
4478 message += L" (timed out)";
4484 m_env.removePlayer(c.peer_id);
4487 // Set player client disconnected
4489 Player *player = m_env.getPlayer(c.peer_id);
4491 player->peer_id = 0;
4495 delete m_clients[c.peer_id];
4496 m_clients.remove(c.peer_id);
4498 // Send player info to all remaining clients
4501 // Send leave chat message to all remaining clients
4502 BroadcastChatMessage(message);
4511 void Server::handlePeerChanges()
4513 while(m_peer_change_queue.size() > 0)
4515 PeerChange c = m_peer_change_queue.pop_front();
4517 dout_server<<"Server: Handling peer change: "
4518 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4521 handlePeerChange(c);
4525 u64 Server::getPlayerPrivs(Player *player)
4529 std::string playername = player->getName();
4530 // Local player gets all privileges regardless of
4531 // what's set on their account.
4532 if(g_settings.get("name") == playername)
4538 return getPlayerAuthPrivs(playername);
4542 void dedicated_server_loop(Server &server, bool &kill)
4544 DSTACK(__FUNCTION_NAME);
4546 dstream<<DTIME<<std::endl;
4547 dstream<<"========================"<<std::endl;
4548 dstream<<"Running dedicated server"<<std::endl;
4549 dstream<<"========================"<<std::endl;
4552 IntervalLimiter m_profiler_interval;
4556 // This is kind of a hack but can be done like this
4557 // because server.step() is very light
4559 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4564 if(server.getShutdownRequested() || kill)
4566 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4573 float profiler_print_interval =
4574 g_settings.getFloat("profiler_print_interval");
4575 if(profiler_print_interval != 0)
4577 if(m_profiler_interval.step(0.030, profiler_print_interval))
4579 dstream<<"Profiler:"<<std::endl;
4580 g_profiler.print(dstream);
4588 static int counter = 0;
4594 core::list<PlayerInfo> list = server.getPlayerInfo();
4595 core::list<PlayerInfo>::Iterator i;
4596 static u32 sum_old = 0;
4597 u32 sum = PIChecksum(list);
4600 dstream<<DTIME<<"Player info:"<<std::endl;
4601 for(i=list.begin(); i!=list.end(); i++)
4603 i->PrintLine(&dstream);