3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
240 if(enable_mapgen_debug_info)
241 infostream<<"EmergeThread: ended up with: "
242 <<analyze_block(block)<<std::endl;
251 Ignore map edit events, they will not need to be
252 sent to anybody because the block hasn't been sent
255 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
257 // Activate objects and stuff
258 m_server->m_env->activateBlock(block, 3600);
263 /*if(block->getLightingExpired()){
264 lighting_invalidated_blocks[block->getPos()] = block;
268 // TODO: Some additional checking and lighting updating,
273 JMutexAutoLock envlock(m_server->m_env_mutex);
278 Collect a list of blocks that have been modified in
279 addition to the fetched one.
283 if(lighting_invalidated_blocks.size() > 0)
285 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
286 <<" blocks"<<std::endl;*/
288 // 50-100ms for single block generation
289 //TimeTaker timer("** EmergeThread updateLighting");
291 // Update lighting without locking the environment mutex,
292 // add modified blocks to changed blocks
293 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
296 // Add all from changed_blocks to modified_blocks
297 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
298 i.atEnd() == false; i++)
300 MapBlock *block = i.getNode()->getValue();
301 modified_blocks.insert(block->getPos(), block);
305 // If we got no block, there should be no invalidated blocks
308 //assert(lighting_invalidated_blocks.size() == 0);
314 Set sent status of modified blocks on clients
317 // NOTE: Server's clients are also behind the connection mutex
318 JMutexAutoLock lock(m_server->m_con_mutex);
321 Add the originally fetched block to the modified list
325 modified_blocks.insert(p, block);
329 Set the modified blocks unsent for all the clients
332 for(core::map<u16, RemoteClient*>::Iterator
333 i = m_server->m_clients.getIterator();
334 i.atEnd() == false; i++)
336 RemoteClient *client = i.getNode()->getValue();
338 if(modified_blocks.size() > 0)
340 // Remove block from sent history
341 client->SetBlocksNotSent(modified_blocks);
347 END_DEBUG_EXCEPTION_HANDLER(errorstream)
352 void RemoteClient::GetNextBlocks(Server *server, float dtime,
353 core::array<PrioritySortedBlockTransfer> &dest)
355 DSTACK(__FUNCTION_NAME);
358 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
361 m_nothing_to_send_pause_timer -= dtime;
362 m_nearest_unsent_reset_timer += dtime;
364 if(m_nothing_to_send_pause_timer >= 0)
369 // Won't send anything if already sending
370 if(m_blocks_sending.size() >= g_settings->getU16
371 ("max_simultaneous_block_sends_per_client"))
373 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
377 //TimeTaker timer("RemoteClient::GetNextBlocks");
379 Player *player = server->m_env->getPlayer(peer_id);
381 assert(player != NULL);
383 v3f playerpos = player->getPosition();
384 v3f playerspeed = player->getSpeed();
385 v3f playerspeeddir(0,0,0);
386 if(playerspeed.getLength() > 1.0*BS)
387 playerspeeddir = playerspeed / playerspeed.getLength();
388 // Predict to next block
389 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
391 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
393 v3s16 center = getNodeBlockPos(center_nodepos);
395 // Camera position and direction
396 v3f camera_pos = player->getEyePosition();
397 v3f camera_dir = v3f(0,0,1);
398 camera_dir.rotateYZBy(player->getPitch());
399 camera_dir.rotateXZBy(player->getYaw());
401 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
402 <<camera_dir.Z<<")"<<std::endl;*/
405 Get the starting value of the block finder radius.
408 if(m_last_center != center)
410 m_nearest_unsent_d = 0;
411 m_last_center = center;
414 /*infostream<<"m_nearest_unsent_reset_timer="
415 <<m_nearest_unsent_reset_timer<<std::endl;*/
417 // Reset periodically to workaround for some bugs or stuff
418 if(m_nearest_unsent_reset_timer > 20.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 //infostream<<"Resetting m_nearest_unsent_d for "
423 // <<server->getPlayerName(peer_id)<<std::endl;
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 s16 max_d_increment_at_time = 2;
467 if(d_max > d_start + max_d_increment_at_time)
468 d_max = d_start + max_d_increment_at_time;
469 /*if(d_max_gen > d_start+2)
470 d_max_gen = d_start+2;*/
472 //infostream<<"Starting from "<<d_start<<std::endl;
474 s32 nearest_emerged_d = -1;
475 s32 nearest_emergefull_d = -1;
476 s32 nearest_sent_d = -1;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 /*errorstream<<"checking d="<<d<<" for "
483 <<server->getPlayerName(peer_id)<<std::endl;*/
484 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
487 If m_nearest_unsent_d was changed by the EmergeThread
488 (it can change it to 0 through SetBlockNotSent),
490 Else update m_nearest_unsent_d
492 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
494 d = m_nearest_unsent_d;
495 last_nearest_unsent_d = m_nearest_unsent_d;
499 Get the border/face dot coordinates of a "d-radiused"
502 core::list<v3s16> list;
503 getFacePositions(list, d);
505 core::list<v3s16>::Iterator li;
506 for(li=list.begin(); li!=list.end(); li++)
508 v3s16 p = *li + center;
512 - Don't allow too many simultaneous transfers
513 - EXCEPT when the blocks are very close
515 Also, don't send blocks that are already flying.
518 // Start with the usual maximum
519 u16 max_simul_dynamic = max_simul_sends_usually;
521 // If block is very close, allow full maximum
522 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
523 max_simul_dynamic = max_simul_sends_setting;
525 // Don't select too many blocks for sending
526 if(num_blocks_selected >= max_simul_dynamic)
528 queue_is_full = true;
529 goto queue_full_break;
532 // Don't send blocks that are currently being transferred
533 if(m_blocks_sending.find(p) != NULL)
539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
547 // If this is true, inexistent block will be made from scratch
548 bool generate = d <= d_max_gen;
551 /*// Limit the generating area vertically to 2/3
552 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
555 // Limit the send area vertically to 1/2
556 if(abs(p.Y - center.Y) > d_max / 2)
562 If block is far away, don't generate it unless it is
568 // Block center y in nodes
569 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 // Don't generate if it's very high or very low
571 if(y < -64 || y > 64)
575 v2s16 p2d_nodes_center(
579 // Get ground height in nodes
580 s16 gh = server->m_env->getServerMap().findGroundLevel(
583 // If differs a lot, don't generate
584 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
586 // Actually, don't even send it
592 //infostream<<"d="<<d<<std::endl;
595 Don't generate or send if not in sight
596 FIXME This only works if the client uses a small enough
597 FOV setting. The default of 72 degrees is fine.
600 float camera_fov = (72.0*PI/180) * 4./3.;
601 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
607 Don't send already sent blocks
610 if(m_blocks_sent.find(p) != NULL)
617 Check if map has this block
619 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
621 bool surely_not_found_on_disk = false;
622 bool block_is_invalid = false;
625 // Reset usage timer, this block will be of use in the future.
626 block->resetUsageTimer();
628 // Block is dummy if data doesn't exist.
629 // It means it has been not found from disk and not generated
632 surely_not_found_on_disk = true;
635 // Block is valid if lighting is up-to-date and data exists
636 if(block->isValid() == false)
638 block_is_invalid = true;
641 /*if(block->isFullyGenerated() == false)
643 block_is_invalid = true;
648 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
649 v2s16 chunkpos = map->sector_to_chunk(p2d);
650 if(map->chunkNonVolatile(chunkpos) == false)
651 block_is_invalid = true;
653 if(block->isGenerated() == false)
654 block_is_invalid = true;
657 If block is not close, don't send it unless it is near
660 Block is near ground level if night-time mesh
661 differs from day-time mesh.
665 if(block->dayNightDiffed() == false)
672 If block has been marked to not exist on disk (dummy)
673 and generating new ones is not wanted, skip block.
675 if(generate == false && surely_not_found_on_disk == true)
682 Add inexistent block to emerge queue.
684 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
686 //TODO: Get value from somewhere
687 // Allow only one block in emerge queue
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
689 // Allow two blocks in queue per client
690 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
691 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
693 //infostream<<"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();
704 if(nearest_emerged_d == -1)
705 nearest_emerged_d = d;
707 if(nearest_emergefull_d == -1)
708 nearest_emergefull_d = d;
715 if(nearest_sent_d == -1)
719 Add block to send queue
722 /*errorstream<<"sending from d="<<d<<" to "
723 <<server->getPlayerName(peer_id)<<std::endl;*/
725 PrioritySortedBlockTransfer q((float)d, p, peer_id);
729 num_blocks_selected += 1;
734 //infostream<<"Stopped at "<<d<<std::endl;
736 // If nothing was found for sending and nothing was queued for
737 // emerging, continue next time browsing from here
738 if(nearest_emerged_d != -1){
739 new_nearest_unsent_d = nearest_emerged_d;
740 } else if(nearest_emergefull_d != -1){
741 new_nearest_unsent_d = nearest_emergefull_d;
743 if(d > g_settings->getS16("max_block_send_distance")){
744 new_nearest_unsent_d = 0;
745 m_nothing_to_send_pause_timer = 2.0;
746 /*infostream<<"GetNextBlocks(): d wrapped around for "
747 <<server->getPlayerName(peer_id)
748 <<"; setting to 0 and pausing"<<std::endl;*/
750 if(nearest_sent_d != -1)
751 new_nearest_unsent_d = nearest_sent_d;
753 new_nearest_unsent_d = d;
757 if(new_nearest_unsent_d != -1)
758 m_nearest_unsent_d = new_nearest_unsent_d;
760 /*timer_result = timer.stop(true);
761 if(timer_result != 0)
762 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
765 void RemoteClient::SendObjectData(
768 core::map<v3s16, bool> &stepped_blocks
771 DSTACK(__FUNCTION_NAME);
773 // Can't send anything without knowing version
774 if(serialization_version == SER_FMT_VER_INVALID)
776 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
782 Send a TOCLIENT_OBJECTDATA packet.
786 u16 number of player positions
798 std::ostringstream os(std::ios_base::binary);
802 writeU16(buf, TOCLIENT_OBJECTDATA);
803 os.write((char*)buf, 2);
806 Get and write player data
809 // Get connected players
810 core::list<Player*> players = server->m_env->getPlayers(true);
812 // Write player count
813 u16 playercount = players.size();
814 writeU16(buf, playercount);
815 os.write((char*)buf, 2);
817 core::list<Player*>::Iterator i;
818 for(i = players.begin();
819 i != players.end(); i++)
823 v3f pf = player->getPosition();
824 v3f sf = player->getSpeed();
826 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
827 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
828 s32 pitch_i (player->getPitch() * 100);
829 s32 yaw_i (player->getYaw() * 100);
831 writeU16(buf, player->peer_id);
832 os.write((char*)buf, 2);
833 writeV3S32(buf, position_i);
834 os.write((char*)buf, 12);
835 writeV3S32(buf, speed_i);
836 os.write((char*)buf, 12);
837 writeS32(buf, pitch_i);
838 os.write((char*)buf, 4);
839 writeS32(buf, yaw_i);
840 os.write((char*)buf, 4);
844 Get and write object data (dummy, for compatibility)
849 os.write((char*)buf, 2);
855 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
858 std::string s = os.str();
859 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
860 // Send as unreliable
861 server->m_con.Send(peer_id, 0, data, false);
864 void RemoteClient::GotBlock(v3s16 p)
866 if(m_blocks_sending.find(p) != NULL)
867 m_blocks_sending.remove(p);
870 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
871 " m_blocks_sending"<<std::endl;*/
872 m_excess_gotblocks++;
874 m_blocks_sent.insert(p, true);
877 void RemoteClient::SentBlock(v3s16 p)
879 if(m_blocks_sending.find(p) == NULL)
880 m_blocks_sending.insert(p, 0.0);
882 infostream<<"RemoteClient::SentBlock(): Sent block"
883 " already in m_blocks_sending"<<std::endl;
886 void RemoteClient::SetBlockNotSent(v3s16 p)
888 m_nearest_unsent_d = 0;
890 if(m_blocks_sending.find(p) != NULL)
891 m_blocks_sending.remove(p);
892 if(m_blocks_sent.find(p) != NULL)
893 m_blocks_sent.remove(p);
896 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
898 m_nearest_unsent_d = 0;
900 for(core::map<v3s16, MapBlock*>::Iterator
901 i = blocks.getIterator();
902 i.atEnd()==false; i++)
904 v3s16 p = i.getNode()->getKey();
906 if(m_blocks_sending.find(p) != NULL)
907 m_blocks_sending.remove(p);
908 if(m_blocks_sent.find(p) != NULL)
909 m_blocks_sent.remove(p);
917 PlayerInfo::PlayerInfo()
923 void PlayerInfo::PrintLine(std::ostream *s)
926 (*s)<<"\""<<name<<"\" ("
927 <<(position.X/10)<<","<<(position.Y/10)
928 <<","<<(position.Z/10)<<") ";
930 (*s)<<" avg_rtt="<<avg_rtt;
934 u32 PIChecksum(core::list<PlayerInfo> &l)
936 core::list<PlayerInfo>::Iterator i;
939 for(i=l.begin(); i!=l.end(); i++)
941 checksum += a * (i->id+1);
942 checksum ^= 0x435aafcd;
953 ModSpec(const std::string &name_="", const std::string path_=""):
959 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
961 core::list<ModSpec> mods;
962 for(core::list<std::string>::Iterator i = modspaths.begin();
963 i != modspaths.end(); i++){
964 std::string modspath = *i;
965 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
966 for(u32 j=0; j<dirlist.size(); j++){
969 std::string modname = dirlist[j].name;
970 std::string modpath = modspath + DIR_DELIM + modname;
971 mods.push_back(ModSpec(modname, modpath));
982 std::string mapsavedir,
983 std::string configpath
986 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
987 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
988 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
990 m_toolmgr(createToolDefManager()),
991 m_nodedef(createNodeDefManager()),
992 m_craftdef(createCraftDefManager()),
994 m_emergethread(this),
996 m_time_of_day_send_timer(0),
998 m_mapsavedir(mapsavedir),
999 m_configpath(configpath),
1000 m_shutdown_requested(false),
1001 m_ignore_map_edit_events(false),
1002 m_ignore_map_edit_events_peer_id(0)
1004 m_liquid_transform_timer = 0.0;
1005 m_print_info_timer = 0.0;
1006 m_objectdata_timer = 0.0;
1007 m_emergethread_trigger_timer = 0.0;
1008 m_savemap_timer = 0.0;
1012 m_step_dtime_mutex.Init();
1015 JMutexAutoLock envlock(m_env_mutex);
1016 JMutexAutoLock conlock(m_con_mutex);
1018 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1020 // Initialize default node definitions
1021 content_mapnode_init(m_nodedef);
1023 // Add default global mod path
1024 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1026 // Initialize scripting
1028 infostream<<"Server: Initializing scripting"<<std::endl;
1029 m_lua = script_init();
1032 scriptapi_export(m_lua, this);
1033 // Load and run scripts
1034 core::list<ModSpec> mods = getMods(m_modspaths);
1035 for(core::list<ModSpec>::Iterator i = mods.begin();
1036 i != mods.end(); i++){
1038 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1039 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1040 bool success = script_load(m_lua, scriptpath.c_str());
1042 errorstream<<"Server: Failed to load and run "
1043 <<scriptpath<<std::endl;
1048 // Initialize Environment
1050 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1052 // Give environment reference to scripting api
1053 scriptapi_add_environment(m_lua, m_env);
1055 // Register us to receive map edit events
1056 m_env->getMap().addEventReceiver(this);
1058 // If file exists, load environment metadata
1059 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1061 infostream<<"Server: Loading environment metadata"<<std::endl;
1062 m_env->loadMeta(m_mapsavedir);
1066 infostream<<"Server: Loading players"<<std::endl;
1067 m_env->deSerializePlayers(m_mapsavedir);
1072 infostream<<"Server::~Server()"<<std::endl;
1075 Send shutdown message
1078 JMutexAutoLock conlock(m_con_mutex);
1080 std::wstring line = L"*** Server shutting down";
1083 Send the message to clients
1085 for(core::map<u16, RemoteClient*>::Iterator
1086 i = m_clients.getIterator();
1087 i.atEnd() == false; i++)
1089 // Get client and check that it is valid
1090 RemoteClient *client = i.getNode()->getValue();
1091 assert(client->peer_id == i.getNode()->getKey());
1092 if(client->serialization_version == SER_FMT_VER_INVALID)
1096 SendChatMessage(client->peer_id, line);
1098 catch(con::PeerNotFoundException &e)
1104 JMutexAutoLock envlock(m_env_mutex);
1109 infostream<<"Server: Saving players"<<std::endl;
1110 m_env->serializePlayers(m_mapsavedir);
1113 Save environment metadata
1115 infostream<<"Server: Saving environment metadata"<<std::endl;
1116 m_env->saveMeta(m_mapsavedir);
1128 JMutexAutoLock clientslock(m_con_mutex);
1130 for(core::map<u16, RemoteClient*>::Iterator
1131 i = m_clients.getIterator();
1132 i.atEnd() == false; i++)
1135 // NOTE: These are removed by env destructor
1137 u16 peer_id = i.getNode()->getKey();
1138 JMutexAutoLock envlock(m_env_mutex);
1139 m_env->removePlayer(peer_id);
1143 delete i.getNode()->getValue();
1147 // Delete Environment
1153 // Deinitialize scripting
1154 infostream<<"Server: Deinitializing scripting"<<std::endl;
1155 script_deinit(m_lua);
1158 void Server::start(unsigned short port)
1160 DSTACK(__FUNCTION_NAME);
1161 // Stop thread if already running
1164 // Initialize connection
1165 m_con.SetTimeoutMs(30);
1169 m_thread.setRun(true);
1172 infostream<<"Server: Started on port "<<port<<std::endl;
1177 DSTACK(__FUNCTION_NAME);
1179 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1181 // Stop threads (set run=false first so both start stopping)
1182 m_thread.setRun(false);
1183 m_emergethread.setRun(false);
1185 m_emergethread.stop();
1187 infostream<<"Server: Threads stopped"<<std::endl;
1190 void Server::step(float dtime)
1192 DSTACK(__FUNCTION_NAME);
1197 JMutexAutoLock lock(m_step_dtime_mutex);
1198 m_step_dtime += dtime;
1202 void Server::AsyncRunStep()
1204 DSTACK(__FUNCTION_NAME);
1206 g_profiler->add("Server::AsyncRunStep (num)", 1);
1210 JMutexAutoLock lock1(m_step_dtime_mutex);
1211 dtime = m_step_dtime;
1215 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1216 // Send blocks to clients
1223 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1225 //infostream<<"Server steps "<<dtime<<std::endl;
1226 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1229 JMutexAutoLock lock1(m_step_dtime_mutex);
1230 m_step_dtime -= dtime;
1237 m_uptime.set(m_uptime.get() + dtime);
1241 // Process connection's timeouts
1242 JMutexAutoLock lock2(m_con_mutex);
1243 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1244 m_con.RunTimeouts(dtime);
1248 // This has to be called so that the client list gets synced
1249 // with the peer list of the connection
1250 handlePeerChanges();
1254 Update m_time_of_day and overall game time
1257 JMutexAutoLock envlock(m_env_mutex);
1259 m_time_counter += dtime;
1260 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1261 u32 units = (u32)(m_time_counter*speed);
1262 m_time_counter -= (f32)units / speed;
1264 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1266 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1269 Send to clients at constant intervals
1272 m_time_of_day_send_timer -= dtime;
1273 if(m_time_of_day_send_timer < 0.0)
1275 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1277 //JMutexAutoLock envlock(m_env_mutex);
1278 JMutexAutoLock conlock(m_con_mutex);
1280 for(core::map<u16, RemoteClient*>::Iterator
1281 i = m_clients.getIterator();
1282 i.atEnd() == false; i++)
1284 RemoteClient *client = i.getNode()->getValue();
1285 //Player *player = m_env->getPlayer(client->peer_id);
1287 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1288 m_env->getTimeOfDay());
1290 m_con.Send(client->peer_id, 0, data, true);
1296 JMutexAutoLock lock(m_env_mutex);
1298 ScopeProfiler sp(g_profiler, "SEnv step");
1299 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1303 const float map_timer_and_unload_dtime = 2.92;
1304 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1306 JMutexAutoLock lock(m_env_mutex);
1307 // Run Map's timers and unload unused data
1308 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1309 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1310 g_settings->getFloat("server_unload_unused_data_timeout"));
1320 m_liquid_transform_timer += dtime;
1321 if(m_liquid_transform_timer >= 1.00)
1323 m_liquid_transform_timer -= 1.00;
1325 JMutexAutoLock lock(m_env_mutex);
1327 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1329 core::map<v3s16, MapBlock*> modified_blocks;
1330 m_env->getMap().transformLiquids(modified_blocks);
1335 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1336 ServerMap &map = ((ServerMap&)m_env->getMap());
1337 map.updateLighting(modified_blocks, lighting_modified_blocks);
1339 // Add blocks modified by lighting to modified_blocks
1340 for(core::map<v3s16, MapBlock*>::Iterator
1341 i = lighting_modified_blocks.getIterator();
1342 i.atEnd() == false; i++)
1344 MapBlock *block = i.getNode()->getValue();
1345 modified_blocks.insert(block->getPos(), block);
1349 Set the modified blocks unsent for all the clients
1352 JMutexAutoLock lock2(m_con_mutex);
1354 for(core::map<u16, RemoteClient*>::Iterator
1355 i = m_clients.getIterator();
1356 i.atEnd() == false; i++)
1358 RemoteClient *client = i.getNode()->getValue();
1360 if(modified_blocks.size() > 0)
1362 // Remove block from sent history
1363 client->SetBlocksNotSent(modified_blocks);
1368 // Periodically print some info
1370 float &counter = m_print_info_timer;
1376 JMutexAutoLock lock2(m_con_mutex);
1378 if(m_clients.size() != 0)
1379 infostream<<"Players:"<<std::endl;
1380 for(core::map<u16, RemoteClient*>::Iterator
1381 i = m_clients.getIterator();
1382 i.atEnd() == false; i++)
1384 //u16 peer_id = i.getNode()->getKey();
1385 RemoteClient *client = i.getNode()->getValue();
1386 Player *player = m_env->getPlayer(client->peer_id);
1389 infostream<<"* "<<player->getName()<<"\t";
1390 client->PrintInfo(infostream);
1395 //if(g_settings->getBool("enable_experimental"))
1399 Check added and deleted active objects
1402 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1403 JMutexAutoLock envlock(m_env_mutex);
1404 JMutexAutoLock conlock(m_con_mutex);
1406 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1408 // Radius inside which objects are active
1409 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1410 radius *= MAP_BLOCKSIZE;
1412 for(core::map<u16, RemoteClient*>::Iterator
1413 i = m_clients.getIterator();
1414 i.atEnd() == false; i++)
1416 RemoteClient *client = i.getNode()->getValue();
1417 Player *player = m_env->getPlayer(client->peer_id);
1420 // This can happen if the client timeouts somehow
1421 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1423 <<" has no associated player"<<std::endl;*/
1426 v3s16 pos = floatToInt(player->getPosition(), BS);
1428 core::map<u16, bool> removed_objects;
1429 core::map<u16, bool> added_objects;
1430 m_env->getRemovedActiveObjects(pos, radius,
1431 client->m_known_objects, removed_objects);
1432 m_env->getAddedActiveObjects(pos, radius,
1433 client->m_known_objects, added_objects);
1435 // Ignore if nothing happened
1436 if(removed_objects.size() == 0 && added_objects.size() == 0)
1438 //infostream<<"active objects: none changed"<<std::endl;
1442 std::string data_buffer;
1446 // Handle removed objects
1447 writeU16((u8*)buf, removed_objects.size());
1448 data_buffer.append(buf, 2);
1449 for(core::map<u16, bool>::Iterator
1450 i = removed_objects.getIterator();
1451 i.atEnd()==false; i++)
1454 u16 id = i.getNode()->getKey();
1455 ServerActiveObject* obj = m_env->getActiveObject(id);
1457 // Add to data buffer for sending
1458 writeU16((u8*)buf, i.getNode()->getKey());
1459 data_buffer.append(buf, 2);
1461 // Remove from known objects
1462 client->m_known_objects.remove(i.getNode()->getKey());
1464 if(obj && obj->m_known_by_count > 0)
1465 obj->m_known_by_count--;
1468 // Handle added objects
1469 writeU16((u8*)buf, added_objects.size());
1470 data_buffer.append(buf, 2);
1471 for(core::map<u16, bool>::Iterator
1472 i = added_objects.getIterator();
1473 i.atEnd()==false; i++)
1476 u16 id = i.getNode()->getKey();
1477 ServerActiveObject* obj = m_env->getActiveObject(id);
1480 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1482 infostream<<"WARNING: "<<__FUNCTION_NAME
1483 <<": NULL object"<<std::endl;
1485 type = obj->getType();
1487 // Add to data buffer for sending
1488 writeU16((u8*)buf, id);
1489 data_buffer.append(buf, 2);
1490 writeU8((u8*)buf, type);
1491 data_buffer.append(buf, 1);
1494 data_buffer.append(serializeLongString(
1495 obj->getClientInitializationData()));
1497 data_buffer.append(serializeLongString(""));
1499 // Add to known objects
1500 client->m_known_objects.insert(i.getNode()->getKey(), false);
1503 obj->m_known_by_count++;
1507 SharedBuffer<u8> reply(2 + data_buffer.size());
1508 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1509 memcpy((char*)&reply[2], data_buffer.c_str(),
1510 data_buffer.size());
1512 m_con.Send(client->peer_id, 0, reply, true);
1514 infostream<<"Server: Sent object remove/add: "
1515 <<removed_objects.size()<<" removed, "
1516 <<added_objects.size()<<" added, "
1517 <<"packet size is "<<reply.getSize()<<std::endl;
1522 Collect a list of all the objects known by the clients
1523 and report it back to the environment.
1526 core::map<u16, bool> all_known_objects;
1528 for(core::map<u16, RemoteClient*>::Iterator
1529 i = m_clients.getIterator();
1530 i.atEnd() == false; i++)
1532 RemoteClient *client = i.getNode()->getValue();
1533 // Go through all known objects of client
1534 for(core::map<u16, bool>::Iterator
1535 i = client->m_known_objects.getIterator();
1536 i.atEnd()==false; i++)
1538 u16 id = i.getNode()->getKey();
1539 all_known_objects[id] = true;
1543 m_env->setKnownActiveObjects(whatever);
1549 Send object messages
1552 JMutexAutoLock envlock(m_env_mutex);
1553 JMutexAutoLock conlock(m_con_mutex);
1555 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1558 // Value = data sent by object
1559 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1561 // Get active object messages from environment
1564 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1568 core::list<ActiveObjectMessage>* message_list = NULL;
1569 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1570 n = buffered_messages.find(aom.id);
1573 message_list = new core::list<ActiveObjectMessage>;
1574 buffered_messages.insert(aom.id, message_list);
1578 message_list = n->getValue();
1580 message_list->push_back(aom);
1583 // Route data to every client
1584 for(core::map<u16, RemoteClient*>::Iterator
1585 i = m_clients.getIterator();
1586 i.atEnd()==false; i++)
1588 RemoteClient *client = i.getNode()->getValue();
1589 std::string reliable_data;
1590 std::string unreliable_data;
1591 // Go through all objects in message buffer
1592 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1593 j = buffered_messages.getIterator();
1594 j.atEnd()==false; j++)
1596 // If object is not known by client, skip it
1597 u16 id = j.getNode()->getKey();
1598 if(client->m_known_objects.find(id) == NULL)
1600 // Get message list of object
1601 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1602 // Go through every message
1603 for(core::list<ActiveObjectMessage>::Iterator
1604 k = list->begin(); k != list->end(); k++)
1606 // Compose the full new data with header
1607 ActiveObjectMessage aom = *k;
1608 std::string new_data;
1611 writeU16((u8*)&buf[0], aom.id);
1612 new_data.append(buf, 2);
1614 new_data += serializeString(aom.datastring);
1615 // Add data to buffer
1617 reliable_data += new_data;
1619 unreliable_data += new_data;
1623 reliable_data and unreliable_data are now ready.
1626 if(reliable_data.size() > 0)
1628 SharedBuffer<u8> reply(2 + reliable_data.size());
1629 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1630 memcpy((char*)&reply[2], reliable_data.c_str(),
1631 reliable_data.size());
1633 m_con.Send(client->peer_id, 0, reply, true);
1635 if(unreliable_data.size() > 0)
1637 SharedBuffer<u8> reply(2 + unreliable_data.size());
1638 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1639 memcpy((char*)&reply[2], unreliable_data.c_str(),
1640 unreliable_data.size());
1641 // Send as unreliable
1642 m_con.Send(client->peer_id, 0, reply, false);
1645 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1647 infostream<<"Server: Size of object message data: "
1648 <<"reliable: "<<reliable_data.size()
1649 <<", unreliable: "<<unreliable_data.size()
1654 // Clear buffered_messages
1655 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1656 i = buffered_messages.getIterator();
1657 i.atEnd()==false; i++)
1659 delete i.getNode()->getValue();
1663 } // enable_experimental
1666 Send queued-for-sending map edit events.
1669 // Don't send too many at a time
1672 // Single change sending is disabled if queue size is not small
1673 bool disable_single_change_sending = false;
1674 if(m_unsent_map_edit_queue.size() >= 4)
1675 disable_single_change_sending = true;
1677 bool got_any_events = false;
1679 // We'll log the amount of each
1682 while(m_unsent_map_edit_queue.size() != 0)
1684 got_any_events = true;
1686 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1688 // Players far away from the change are stored here.
1689 // Instead of sending the changes, MapBlocks are set not sent
1691 core::list<u16> far_players;
1693 if(event->type == MEET_ADDNODE)
1695 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1696 prof.add("MEET_ADDNODE", 1);
1697 if(disable_single_change_sending)
1698 sendAddNode(event->p, event->n, event->already_known_by_peer,
1701 sendAddNode(event->p, event->n, event->already_known_by_peer,
1704 else if(event->type == MEET_REMOVENODE)
1706 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1707 prof.add("MEET_REMOVENODE", 1);
1708 if(disable_single_change_sending)
1709 sendRemoveNode(event->p, event->already_known_by_peer,
1712 sendRemoveNode(event->p, event->already_known_by_peer,
1715 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1717 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1718 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1719 setBlockNotSent(event->p);
1721 else if(event->type == MEET_OTHER)
1723 infostream<<"Server: MEET_OTHER"<<std::endl;
1724 prof.add("MEET_OTHER", 1);
1725 for(core::map<v3s16, bool>::Iterator
1726 i = event->modified_blocks.getIterator();
1727 i.atEnd()==false; i++)
1729 v3s16 p = i.getNode()->getKey();
1735 prof.add("unknown", 1);
1736 infostream<<"WARNING: Server: Unknown MapEditEvent "
1737 <<((u32)event->type)<<std::endl;
1741 Set blocks not sent to far players
1743 if(far_players.size() > 0)
1745 // Convert list format to that wanted by SetBlocksNotSent
1746 core::map<v3s16, MapBlock*> modified_blocks2;
1747 for(core::map<v3s16, bool>::Iterator
1748 i = event->modified_blocks.getIterator();
1749 i.atEnd()==false; i++)
1751 v3s16 p = i.getNode()->getKey();
1752 modified_blocks2.insert(p,
1753 m_env->getMap().getBlockNoCreateNoEx(p));
1755 // Set blocks not sent
1756 for(core::list<u16>::Iterator
1757 i = far_players.begin();
1758 i != far_players.end(); i++)
1761 RemoteClient *client = getClient(peer_id);
1764 client->SetBlocksNotSent(modified_blocks2);
1770 /*// Don't send too many at a time
1772 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1778 infostream<<"Server: MapEditEvents:"<<std::endl;
1779 prof.print(infostream);
1785 Send object positions
1788 float &counter = m_objectdata_timer;
1790 if(counter >= g_settings->getFloat("objectdata_interval"))
1792 JMutexAutoLock lock1(m_env_mutex);
1793 JMutexAutoLock lock2(m_con_mutex);
1795 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1797 SendObjectData(counter);
1804 Trigger emergethread (it somehow gets to a non-triggered but
1805 bysy state sometimes)
1808 float &counter = m_emergethread_trigger_timer;
1814 m_emergethread.trigger();
1818 // Save map, players and auth stuff
1820 float &counter = m_savemap_timer;
1822 if(counter >= g_settings->getFloat("server_map_save_interval"))
1826 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1829 if(m_authmanager.isModified())
1830 m_authmanager.save();
1833 if(m_banmanager.isModified())
1834 m_banmanager.save();
1837 JMutexAutoLock lock(m_env_mutex);
1839 /*// Unload unused data (delete from memory)
1840 m_env->getMap().unloadUnusedData(
1841 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1843 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1844 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1847 // Save only changed parts
1848 m_env->getMap().save(true);
1850 /*if(deleted_count > 0)
1852 infostream<<"Server: Unloaded "<<deleted_count
1853 <<" blocks from memory"<<std::endl;
1857 m_env->serializePlayers(m_mapsavedir);
1859 // Save environment metadata
1860 m_env->saveMeta(m_mapsavedir);
1865 void Server::Receive()
1867 DSTACK(__FUNCTION_NAME);
1868 SharedBuffer<u8> data;
1873 JMutexAutoLock conlock(m_con_mutex);
1874 datasize = m_con.Receive(peer_id, data);
1877 // This has to be called so that the client list gets synced
1878 // with the peer list of the connection
1879 handlePeerChanges();
1881 ProcessData(*data, datasize, peer_id);
1883 catch(con::InvalidIncomingDataException &e)
1885 infostream<<"Server::Receive(): "
1886 "InvalidIncomingDataException: what()="
1887 <<e.what()<<std::endl;
1889 catch(con::PeerNotFoundException &e)
1891 //NOTE: This is not needed anymore
1893 // The peer has been disconnected.
1894 // Find the associated player and remove it.
1896 /*JMutexAutoLock envlock(m_env_mutex);
1898 infostream<<"ServerThread: peer_id="<<peer_id
1899 <<" has apparently closed connection. "
1900 <<"Removing player."<<std::endl;
1902 m_env->removePlayer(peer_id);*/
1906 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1908 DSTACK(__FUNCTION_NAME);
1909 // Environment is locked first.
1910 JMutexAutoLock envlock(m_env_mutex);
1911 JMutexAutoLock conlock(m_con_mutex);
1914 Address address = m_con.GetPeerAddress(peer_id);
1916 // drop player if is ip is banned
1917 if(m_banmanager.isIpBanned(address.serializeString())){
1918 SendAccessDenied(m_con, peer_id,
1919 L"Your ip is banned. Banned name was "
1920 +narrow_to_wide(m_banmanager.getBanName(
1921 address.serializeString())));
1922 m_con.DeletePeer(peer_id);
1926 catch(con::PeerNotFoundException &e)
1928 infostream<<"Server::ProcessData(): Cancelling: peer "
1929 <<peer_id<<" not found"<<std::endl;
1933 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1941 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1943 if(command == TOSERVER_INIT)
1945 // [0] u16 TOSERVER_INIT
1946 // [2] u8 SER_FMT_VER_HIGHEST
1947 // [3] u8[20] player_name
1948 // [23] u8[28] password <--- can be sent without this, from old versions
1950 if(datasize < 2+1+PLAYERNAME_SIZE)
1953 infostream<<"Server: Got TOSERVER_INIT from "
1954 <<peer_id<<std::endl;
1956 // First byte after command is maximum supported
1957 // serialization version
1958 u8 client_max = data[2];
1959 u8 our_max = SER_FMT_VER_HIGHEST;
1960 // Use the highest version supported by both
1961 u8 deployed = core::min_(client_max, our_max);
1962 // If it's lower than the lowest supported, give up.
1963 if(deployed < SER_FMT_VER_LOWEST)
1964 deployed = SER_FMT_VER_INVALID;
1966 //peer->serialization_version = deployed;
1967 getClient(peer_id)->pending_serialization_version = deployed;
1969 if(deployed == SER_FMT_VER_INVALID)
1971 infostream<<"Server: Cannot negotiate "
1972 "serialization version with peer "
1973 <<peer_id<<std::endl;
1974 SendAccessDenied(m_con, peer_id,
1975 L"Your client is too old (map format)");
1980 Read and check network protocol version
1983 u16 net_proto_version = 0;
1984 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1986 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1989 getClient(peer_id)->net_proto_version = net_proto_version;
1991 if(net_proto_version == 0)
1993 SendAccessDenied(m_con, peer_id,
1994 L"Your client is too old. Please upgrade.");
1998 /* Uhh... this should actually be a warning but let's do it like this */
1999 if(g_settings->getBool("strict_protocol_version_checking"))
2001 if(net_proto_version < PROTOCOL_VERSION)
2003 SendAccessDenied(m_con, peer_id,
2004 L"Your client is too old. Please upgrade.");
2014 char playername[PLAYERNAME_SIZE];
2015 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2017 playername[i] = data[3+i];
2019 playername[PLAYERNAME_SIZE-1] = 0;
2021 if(playername[0]=='\0')
2023 infostream<<"Server: Player has empty name"<<std::endl;
2024 SendAccessDenied(m_con, peer_id,
2029 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2031 infostream<<"Server: Player has invalid name"<<std::endl;
2032 SendAccessDenied(m_con, peer_id,
2033 L"Name contains unallowed characters");
2038 char password[PASSWORD_SIZE];
2039 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2041 // old version - assume blank password
2046 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2048 password[i] = data[23+i];
2050 password[PASSWORD_SIZE-1] = 0;
2053 std::string checkpwd;
2054 if(m_authmanager.exists(playername))
2056 checkpwd = m_authmanager.getPassword(playername);
2060 checkpwd = g_settings->get("default_password");
2063 /*infostream<<"Server: Client gave password '"<<password
2064 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2066 if(password != checkpwd && m_authmanager.exists(playername))
2068 infostream<<"Server: peer_id="<<peer_id
2069 <<": supplied invalid password for "
2070 <<playername<<std::endl;
2071 SendAccessDenied(m_con, peer_id, L"Invalid password");
2075 // Add player to auth manager
2076 if(m_authmanager.exists(playername) == false)
2078 infostream<<"Server: adding player "<<playername
2079 <<" to auth manager"<<std::endl;
2080 m_authmanager.add(playername);
2081 m_authmanager.setPassword(playername, checkpwd);
2082 m_authmanager.setPrivs(playername,
2083 stringToPrivs(g_settings->get("default_privs")));
2084 m_authmanager.save();
2087 // Enforce user limit.
2088 // Don't enforce for users that have some admin right
2089 if(m_clients.size() >= g_settings->getU16("max_users") &&
2090 (m_authmanager.getPrivs(playername)
2091 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2092 playername != g_settings->get("name"))
2094 SendAccessDenied(m_con, peer_id, L"Too many users.");
2099 Player *player = emergePlayer(playername, password, peer_id);
2101 // If failed, cancel
2104 infostream<<"Server: peer_id="<<peer_id
2105 <<": failed to emerge player"<<std::endl;
2110 Answer with a TOCLIENT_INIT
2113 SharedBuffer<u8> reply(2+1+6+8);
2114 writeU16(&reply[0], TOCLIENT_INIT);
2115 writeU8(&reply[2], deployed);
2116 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2117 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2120 m_con.Send(peer_id, 0, reply, true);
2124 Send complete position information
2126 SendMovePlayer(player);
2131 if(command == TOSERVER_INIT2)
2133 infostream<<"Server: Got TOSERVER_INIT2 from "
2134 <<peer_id<<std::endl;
2137 getClient(peer_id)->serialization_version
2138 = getClient(peer_id)->pending_serialization_version;
2141 Send some initialization data
2144 // Send tool definitions
2145 SendToolDef(m_con, peer_id, m_toolmgr);
2147 // Send node definitions
2148 SendNodeDef(m_con, peer_id, m_nodedef);
2151 SendTextures(peer_id);
2153 // Send player info to all players
2156 // Send inventory to player
2157 UpdateCrafting(peer_id);
2158 SendInventory(peer_id);
2160 // Send player items to all players
2163 Player *player = m_env->getPlayer(peer_id);
2166 SendPlayerHP(player);
2170 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2171 m_env->getTimeOfDay());
2172 m_con.Send(peer_id, 0, data, true);
2175 // Send information about server to player in chat
2176 SendChatMessage(peer_id, getStatusString());
2178 // Send information about joining in chat
2180 std::wstring name = L"unknown";
2181 Player *player = m_env->getPlayer(peer_id);
2183 name = narrow_to_wide(player->getName());
2185 std::wstring message;
2188 message += L" joined game";
2189 BroadcastChatMessage(message);
2192 // Warnings about protocol version can be issued here
2193 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2195 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2199 Check HP, respawn if necessary
2201 HandlePlayerHP(player, 0);
2207 std::ostringstream os(std::ios_base::binary);
2208 for(core::map<u16, RemoteClient*>::Iterator
2209 i = m_clients.getIterator();
2210 i.atEnd() == false; i++)
2212 RemoteClient *client = i.getNode()->getValue();
2213 assert(client->peer_id == i.getNode()->getKey());
2214 if(client->serialization_version == SER_FMT_VER_INVALID)
2217 Player *player = m_env->getPlayer(client->peer_id);
2220 // Get name of player
2221 os<<player->getName()<<" ";
2224 actionstream<<player->getName()<<" joins game. List of players: "
2225 <<os.str()<<std::endl;
2231 if(peer_ser_ver == SER_FMT_VER_INVALID)
2233 infostream<<"Server::ProcessData(): Cancelling: Peer"
2234 " serialization format invalid or not initialized."
2235 " Skipping incoming command="<<command<<std::endl;
2239 Player *player = m_env->getPlayer(peer_id);
2242 infostream<<"Server::ProcessData(): Cancelling: "
2243 "No player for peer_id="<<peer_id
2247 if(command == TOSERVER_PLAYERPOS)
2249 if(datasize < 2+12+12+4+4)
2253 v3s32 ps = readV3S32(&data[start+2]);
2254 v3s32 ss = readV3S32(&data[start+2+12]);
2255 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2256 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2257 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2258 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2259 pitch = wrapDegrees(pitch);
2260 yaw = wrapDegrees(yaw);
2262 player->setPosition(position);
2263 player->setSpeed(speed);
2264 player->setPitch(pitch);
2265 player->setYaw(yaw);
2267 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2268 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2269 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2271 else if(command == TOSERVER_GOTBLOCKS)
2284 u16 count = data[2];
2285 for(u16 i=0; i<count; i++)
2287 if((s16)datasize < 2+1+(i+1)*6)
2288 throw con::InvalidIncomingDataException
2289 ("GOTBLOCKS length is too short");
2290 v3s16 p = readV3S16(&data[2+1+i*6]);
2291 /*infostream<<"Server: GOTBLOCKS ("
2292 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2293 RemoteClient *client = getClient(peer_id);
2294 client->GotBlock(p);
2297 else if(command == TOSERVER_DELETEDBLOCKS)
2310 u16 count = data[2];
2311 for(u16 i=0; i<count; i++)
2313 if((s16)datasize < 2+1+(i+1)*6)
2314 throw con::InvalidIncomingDataException
2315 ("DELETEDBLOCKS length is too short");
2316 v3s16 p = readV3S16(&data[2+1+i*6]);
2317 /*infostream<<"Server: DELETEDBLOCKS ("
2318 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2319 RemoteClient *client = getClient(peer_id);
2320 client->SetBlockNotSent(p);
2323 else if(command == TOSERVER_CLICK_OBJECT)
2325 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2328 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2333 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2339 [2] u8 button (0=left, 1=right)
2343 u8 button = readU8(&data[2]);
2344 u16 id = readS16(&data[3]);
2345 u16 item_i = readU16(&data[5]);
2347 ServerActiveObject *obj = m_env->getActiveObject(id);
2351 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2356 // Skip if object has been removed
2360 //TODO: Check that object is reasonably close
2362 // Get ServerRemotePlayer
2363 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2365 // Update wielded item
2366 srp->wieldItem(item_i);
2368 // Left click, pick/punch
2371 actionstream<<player->getName()<<" punches object "
2372 <<obj->getId()<<std::endl;
2379 Try creating inventory item
2381 InventoryItem *item = obj->createPickedUpItem();
2385 InventoryList *ilist = player->inventory.getList("main");
2388 actionstream<<player->getName()<<" picked up "
2389 <<item->getName()<<std::endl;
2390 if(g_settings->getBool("creative_mode") == false)
2392 // Skip if inventory has no free space
2393 if(ilist->roomForItem(item) == false)
2395 infostream<<"Player inventory has no free space"<<std::endl;
2399 // Add to inventory and send inventory
2400 ilist->addItem(item);
2401 UpdateCrafting(player->peer_id);
2402 SendInventory(player->peer_id);
2405 // Remove object from environment
2406 obj->m_removed = true;
2412 Item cannot be picked up. Punch it instead.
2415 actionstream<<player->getName()<<" punches object "
2416 <<obj->getId()<<std::endl;
2418 ToolItem *titem = NULL;
2419 std::string toolname = "";
2421 InventoryList *mlist = player->inventory.getList("main");
2424 InventoryItem *item = mlist->getItem(item_i);
2425 if(item && (std::string)item->getName() == "ToolItem")
2427 titem = (ToolItem*)item;
2428 toolname = titem->getToolName();
2432 v3f playerpos = player->getPosition();
2433 v3f objpos = obj->getBasePosition();
2434 v3f dir = (objpos - playerpos).normalize();
2436 u16 wear = obj->punch(toolname, dir, player->getName());
2440 bool weared_out = titem->addWear(wear);
2442 mlist->deleteItem(item_i);
2443 SendInventory(player->peer_id);
2448 // Right click, do something with object
2451 actionstream<<player->getName()<<" right clicks object "
2452 <<obj->getId()<<std::endl;
2455 obj->rightClick(srp);
2459 Update player state to client
2461 SendPlayerHP(player);
2462 UpdateCrafting(player->peer_id);
2463 SendInventory(player->peer_id);
2465 else if(command == TOSERVER_GROUND_ACTION)
2473 [3] v3s16 nodepos_undersurface
2474 [9] v3s16 nodepos_abovesurface
2479 2: stop digging (all parameters ignored)
2480 3: digging completed
2482 u8 action = readU8(&data[2]);
2484 p_under.X = readS16(&data[3]);
2485 p_under.Y = readS16(&data[5]);
2486 p_under.Z = readS16(&data[7]);
2488 p_over.X = readS16(&data[9]);
2489 p_over.Y = readS16(&data[11]);
2490 p_over.Z = readS16(&data[13]);
2491 u16 item_i = readU16(&data[15]);
2493 //TODO: Check that target is reasonably close
2501 NOTE: This can be used in the future to check if
2502 somebody is cheating, by checking the timing.
2504 bool cannot_punch_node = false;
2506 MapNode n(CONTENT_IGNORE);
2510 n = m_env->getMap().getNode(p_under);
2512 catch(InvalidPositionException &e)
2514 infostream<<"Server: Not punching: Node not found."
2515 <<" Adding block to emerge queue."
2517 m_emerge_queue.addBlock(peer_id,
2518 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2519 cannot_punch_node = true;
2522 if(cannot_punch_node)
2528 scriptapi_environment_on_punchnode(m_lua, p_under, n);
2535 else if(action == 2)
2538 RemoteClient *client = getClient(peer_id);
2539 JMutexAutoLock digmutex(client->m_dig_mutex);
2540 client->m_dig_tool_item = -1;
2545 3: Digging completed
2547 else if(action == 3)
2549 // Mandatory parameter; actually used for nothing
2550 core::map<v3s16, MapBlock*> modified_blocks;
2552 content_t material = CONTENT_IGNORE;
2553 u8 mineral = MINERAL_NONE;
2555 bool cannot_remove_node = false;
2557 MapNode n(CONTENT_IGNORE);
2560 n = m_env->getMap().getNode(p_under);
2562 mineral = n.getMineral(m_nodedef);
2563 // Get material at position
2564 material = n.getContent();
2565 // If not yet cancelled
2566 if(cannot_remove_node == false)
2568 // If it's not diggable, do nothing
2569 if(m_nodedef->get(material).diggable == false)
2571 infostream<<"Server: Not finishing digging: "
2572 <<"Node not diggable"
2574 cannot_remove_node = true;
2577 // If not yet cancelled
2578 if(cannot_remove_node == false)
2580 // Get node metadata
2581 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2582 if(meta && meta->nodeRemovalDisabled() == true)
2584 infostream<<"Server: Not finishing digging: "
2585 <<"Node metadata disables removal"
2587 cannot_remove_node = true;
2591 catch(InvalidPositionException &e)
2593 infostream<<"Server: Not finishing digging: Node not found."
2594 <<" Adding block to emerge queue."
2596 m_emerge_queue.addBlock(peer_id,
2597 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2598 cannot_remove_node = true;
2601 // Make sure the player is allowed to do it
2602 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2604 infostream<<"Player "<<player->getName()<<" cannot remove node"
2605 <<" because privileges are "<<getPlayerPrivs(player)
2607 cannot_remove_node = true;
2611 If node can't be removed, set block to be re-sent to
2614 if(cannot_remove_node)
2616 infostream<<"Server: Not finishing digging."<<std::endl;
2618 // Client probably has wrong data.
2619 // Set block not sent, so that client will get
2621 infostream<<"Client "<<peer_id<<" tried to dig "
2622 <<"node; but node cannot be removed."
2623 <<" setting MapBlock not sent."<<std::endl;
2624 RemoteClient *client = getClient(peer_id);
2625 v3s16 blockpos = getNodeBlockPos(p_under);
2626 client->SetBlockNotSent(blockpos);
2631 actionstream<<player->getName()<<" digs "<<PP(p_under)
2632 <<", gets material "<<(int)material<<", mineral "
2633 <<(int)mineral<<std::endl;
2636 Send the removal to all close-by players.
2637 - If other player is close, send REMOVENODE
2638 - Otherwise set blocks not sent
2640 core::list<u16> far_players;
2641 sendRemoveNode(p_under, peer_id, &far_players, 30);
2644 Update and send inventory
2647 if(g_settings->getBool("creative_mode") == false)
2652 InventoryList *mlist = player->inventory.getList("main");
2655 InventoryItem *item = mlist->getItem(item_i);
2656 if(item && (std::string)item->getName() == "ToolItem")
2658 ToolItem *titem = (ToolItem*)item;
2659 std::string toolname = titem->getToolName();
2661 // Get digging properties for material and tool
2662 ToolDiggingProperties tp =
2663 m_toolmgr->getDiggingProperties(toolname);
2664 DiggingProperties prop =
2665 getDiggingProperties(material, &tp, m_nodedef);
2667 if(prop.diggable == false)
2669 infostream<<"Server: WARNING: Player digged"
2670 <<" with impossible material + tool"
2671 <<" combination"<<std::endl;
2674 bool weared_out = titem->addWear(prop.wear);
2678 mlist->deleteItem(item_i);
2684 Add dug item to inventory
2687 InventoryItem *item = NULL;
2689 if(mineral != MINERAL_NONE)
2690 item = getDiggedMineralItem(mineral, this);
2695 const std::string &dug_s = m_nodedef->get(material).dug_item;
2698 std::istringstream is(dug_s, std::ios::binary);
2699 item = InventoryItem::deSerialize(is, this);
2705 // Add a item to inventory
2706 player->inventory.addItem("main", item);
2709 UpdateCrafting(player->peer_id);
2710 SendInventory(player->peer_id);
2715 if(mineral != MINERAL_NONE)
2716 item = getDiggedMineralItem(mineral, this);
2721 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2722 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2723 if(extra_dug_s != "" && extra_rarity != 0
2724 && myrand() % extra_rarity == 0)
2726 std::istringstream is(extra_dug_s, std::ios::binary);
2727 item = InventoryItem::deSerialize(is, this);
2733 // Add a item to inventory
2734 player->inventory.addItem("main", item);
2737 UpdateCrafting(player->peer_id);
2738 SendInventory(player->peer_id);
2744 (this takes some time so it is done after the quick stuff)
2747 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2749 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2752 Set blocks not sent to far players
2754 for(core::list<u16>::Iterator
2755 i = far_players.begin();
2756 i != far_players.end(); i++)
2759 RemoteClient *client = getClient(peer_id);
2762 client->SetBlocksNotSent(modified_blocks);
2768 scriptapi_environment_on_dignode(m_lua, p_under, n);
2774 else if(action == 1)
2777 InventoryList *ilist = player->inventory.getList("main");
2782 InventoryItem *item = ilist->getItem(item_i);
2784 // If there is no item, it is not possible to add it anywhere
2789 Handle material items
2791 if(std::string("MaterialItem") == item->getName())
2794 // Don't add a node if this is not a free space
2795 MapNode n2 = m_env->getMap().getNode(p_over);
2796 bool no_enough_privs =
2797 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2799 infostream<<"Player "<<player->getName()<<" cannot add node"
2800 <<" because privileges are "<<getPlayerPrivs(player)
2803 if(m_nodedef->get(n2).buildable_to == false
2806 // Client probably has wrong data.
2807 // Set block not sent, so that client will get
2809 infostream<<"Client "<<peer_id<<" tried to place"
2810 <<" node in invalid position; setting"
2811 <<" MapBlock not sent."<<std::endl;
2812 RemoteClient *client = getClient(peer_id);
2813 v3s16 blockpos = getNodeBlockPos(p_over);
2814 client->SetBlockNotSent(blockpos);
2818 catch(InvalidPositionException &e)
2820 infostream<<"Server: Ignoring ADDNODE: Node not found"
2821 <<" Adding block to emerge queue."
2823 m_emerge_queue.addBlock(peer_id,
2824 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2828 // Reset build time counter
2829 getClient(peer_id)->m_time_from_building = 0.0;
2832 MaterialItem *mitem = (MaterialItem*)item;
2834 n.setContent(mitem->getMaterial());
2836 actionstream<<player->getName()<<" places material "
2837 <<(int)mitem->getMaterial()
2838 <<" at "<<PP(p_under)<<std::endl;
2840 // Calculate direction for wall mounted stuff
2841 if(m_nodedef->get(n).wall_mounted)
2842 n.param2 = packDir(p_under - p_over);
2844 // Calculate the direction for furnaces and chests and stuff
2845 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2847 v3f playerpos = player->getPosition();
2848 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2849 blockpos = blockpos.normalize();
2851 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2865 Send to all close-by players
2867 core::list<u16> far_players;
2868 sendAddNode(p_over, n, 0, &far_players, 30);
2873 InventoryList *ilist = player->inventory.getList("main");
2874 if(g_settings->getBool("creative_mode") == false && ilist)
2876 // Remove from inventory and send inventory
2877 if(mitem->getCount() == 1)
2878 ilist->deleteItem(item_i);
2882 UpdateCrafting(peer_id);
2883 SendInventory(peer_id);
2889 This takes some time so it is done after the quick stuff
2891 core::map<v3s16, MapBlock*> modified_blocks;
2893 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2895 std::string p_name = std::string(player->getName());
2896 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2899 Set blocks not sent to far players
2901 for(core::list<u16>::Iterator
2902 i = far_players.begin();
2903 i != far_players.end(); i++)
2906 RemoteClient *client = getClient(peer_id);
2909 client->SetBlocksNotSent(modified_blocks);
2915 scriptapi_environment_on_placenode(m_lua, p_over, n);
2918 Calculate special events
2921 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
2924 for(s16 z=-1; z<=1; z++)
2925 for(s16 y=-1; y<=1; y++)
2926 for(s16 x=-1; x<=1; x++)
2933 Place other item (not a block)
2937 v3s16 blockpos = getNodeBlockPos(p_over);
2940 Check that the block is loaded so that the item
2941 can properly be added to the static list too
2943 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2946 infostream<<"Error while placing object: "
2947 "block not found"<<std::endl;
2952 If in creative mode, item dropping is disabled unless
2953 player has build privileges
2955 if(g_settings->getBool("creative_mode") &&
2956 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2958 infostream<<"Not allowing player to drop item: "
2959 "creative mode and no build privs"<<std::endl;
2963 // Calculate a position for it
2964 v3f pos = intToFloat(p_over, BS);
2966 /*pos.Y -= BS*0.25; // let it drop a bit
2968 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2969 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2974 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2978 infostream<<"WARNING: item resulted in NULL object, "
2979 <<"not placing onto map"
2984 actionstream<<player->getName()<<" places "<<item->getName()
2985 <<" at "<<PP(p_over)<<std::endl;
2987 // Add the object to the environment
2988 m_env->addActiveObject(obj);
2990 infostream<<"Placed object"<<std::endl;
2992 if(g_settings->getBool("creative_mode") == false)
2994 // Delete the right amount of items from the slot
2995 u16 dropcount = item->getDropCount();
2997 // Delete item if all gone
2998 if(item->getCount() <= dropcount)
3000 if(item->getCount() < dropcount)
3001 infostream<<"WARNING: Server: dropped more items"
3002 <<" than the slot contains"<<std::endl;
3004 InventoryList *ilist = player->inventory.getList("main");
3006 // Remove from inventory and send inventory
3007 ilist->deleteItem(item_i);
3009 // Else decrement it
3011 item->remove(dropcount);
3014 UpdateCrafting(peer_id);
3015 SendInventory(peer_id);
3023 Catch invalid actions
3027 infostream<<"WARNING: Server: Invalid action "
3028 <<action<<std::endl;
3032 else if(command == TOSERVER_RELEASE)
3041 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3044 else if(command == TOSERVER_SIGNTEXT)
3046 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3050 else if(command == TOSERVER_SIGNNODETEXT)
3052 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3060 std::string datastring((char*)&data[2], datasize-2);
3061 std::istringstream is(datastring, std::ios_base::binary);
3064 is.read((char*)buf, 6);
3065 v3s16 p = readV3S16(buf);
3066 is.read((char*)buf, 2);
3067 u16 textlen = readU16(buf);
3069 for(u16 i=0; i<textlen; i++)
3071 is.read((char*)buf, 1);
3072 text += (char)buf[0];
3075 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3078 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3080 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3081 signmeta->setText(text);
3083 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3084 <<" at "<<PP(p)<<std::endl;
3086 v3s16 blockpos = getNodeBlockPos(p);
3087 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3090 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3094 setBlockNotSent(blockpos);
3096 else if(command == TOSERVER_INVENTORY_ACTION)
3098 /*// Ignore inventory changes if in creative mode
3099 if(g_settings->getBool("creative_mode") == true)
3101 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3105 // Strip command and create a stream
3106 std::string datastring((char*)&data[2], datasize-2);
3107 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3108 std::istringstream is(datastring, std::ios_base::binary);
3110 InventoryAction *a = InventoryAction::deSerialize(is);
3115 c.current_player = player;
3118 Handle craftresult specially if not in creative mode
3120 bool disable_action = false;
3121 if(a->getType() == IACTION_MOVE
3122 && g_settings->getBool("creative_mode") == false)
3124 IMoveAction *ma = (IMoveAction*)a;
3125 if(ma->to_inv == "current_player" &&
3126 ma->from_inv == "current_player")
3128 InventoryList *rlist = player->inventory.getList("craftresult");
3130 InventoryList *clist = player->inventory.getList("craft");
3132 InventoryList *mlist = player->inventory.getList("main");
3135 Craftresult is no longer preview if something
3138 if(ma->to_list == "craftresult"
3139 && ma->from_list != "craftresult")
3141 // If it currently is a preview, remove
3143 if(player->craftresult_is_preview)
3145 rlist->deleteItem(0);
3147 player->craftresult_is_preview = false;
3150 Crafting takes place if this condition is true.
3152 if(player->craftresult_is_preview &&
3153 ma->from_list == "craftresult")
3155 player->craftresult_is_preview = false;
3156 clist->decrementMaterials(1);
3158 /* Print out action */
3159 InventoryList *list =
3160 player->inventory.getList("craftresult");
3162 InventoryItem *item = list->getItem(0);
3163 std::string itemname = "NULL";
3165 itemname = item->getName();
3166 actionstream<<player->getName()<<" crafts "
3167 <<itemname<<std::endl;
3170 If the craftresult is placed on itself, move it to
3171 main inventory instead of doing the action
3173 if(ma->to_list == "craftresult"
3174 && ma->from_list == "craftresult")
3176 disable_action = true;
3178 InventoryItem *item1 = rlist->changeItem(0, NULL);
3179 mlist->addItem(item1);
3182 // Disallow moving items if not allowed to build
3183 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3187 // if it's a locking chest, only allow the owner or server admins to move items
3188 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3190 Strfnd fn(ma->from_inv);
3191 std::string id0 = fn.next(":");
3192 if(id0 == "nodemeta")
3195 p.X = stoi(fn.next(","));
3196 p.Y = stoi(fn.next(","));
3197 p.Z = stoi(fn.next(","));
3198 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3199 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3200 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3201 if (lcm->getOwner() != player->getName())
3206 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3208 Strfnd fn(ma->to_inv);
3209 std::string id0 = fn.next(":");
3210 if(id0 == "nodemeta")
3213 p.X = stoi(fn.next(","));
3214 p.Y = stoi(fn.next(","));
3215 p.Z = stoi(fn.next(","));
3216 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3217 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3218 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3219 if (lcm->getOwner() != player->getName())
3226 if(disable_action == false)
3228 // Feed action to player inventory
3236 UpdateCrafting(player->peer_id);
3237 SendInventory(player->peer_id);
3242 infostream<<"TOSERVER_INVENTORY_ACTION: "
3243 <<"InventoryAction::deSerialize() returned NULL"
3247 else if(command == TOSERVER_CHAT_MESSAGE)
3255 std::string datastring((char*)&data[2], datasize-2);
3256 std::istringstream is(datastring, std::ios_base::binary);
3259 is.read((char*)buf, 2);
3260 u16 len = readU16(buf);
3262 std::wstring message;
3263 for(u16 i=0; i<len; i++)
3265 is.read((char*)buf, 2);
3266 message += (wchar_t)readU16(buf);
3269 // Get player name of this client
3270 std::wstring name = narrow_to_wide(player->getName());
3272 // Line to send to players
3274 // Whether to send to the player that sent the line
3275 bool send_to_sender = false;
3276 // Whether to send to other players
3277 bool send_to_others = false;
3279 // Local player gets all privileges regardless of
3280 // what's set on their account.
3281 u64 privs = getPlayerPrivs(player);
3284 if(message[0] == L'/')
3286 size_t strip_size = 1;
3287 if (message[1] == L'#') // support old-style commans
3289 message = message.substr(strip_size);
3291 WStrfnd f1(message);
3292 f1.next(L" "); // Skip over /#whatever
3293 std::wstring paramstring = f1.next(L"");
3295 ServerCommandContext *ctx = new ServerCommandContext(
3296 str_split(message, L' '),
3303 std::wstring reply(processServerCommand(ctx));
3304 send_to_sender = ctx->flags & SEND_TO_SENDER;
3305 send_to_others = ctx->flags & SEND_TO_OTHERS;
3307 if (ctx->flags & SEND_NO_PREFIX)
3310 line += L"Server: " + reply;
3317 if(privs & PRIV_SHOUT)
3323 send_to_others = true;
3327 line += L"Server: You are not allowed to shout";
3328 send_to_sender = true;
3335 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3338 Send the message to clients
3340 for(core::map<u16, RemoteClient*>::Iterator
3341 i = m_clients.getIterator();
3342 i.atEnd() == false; i++)
3344 // Get client and check that it is valid
3345 RemoteClient *client = i.getNode()->getValue();
3346 assert(client->peer_id == i.getNode()->getKey());
3347 if(client->serialization_version == SER_FMT_VER_INVALID)
3351 bool sender_selected = (peer_id == client->peer_id);
3352 if(sender_selected == true && send_to_sender == false)
3354 if(sender_selected == false && send_to_others == false)
3357 SendChatMessage(client->peer_id, line);
3361 else if(command == TOSERVER_DAMAGE)
3363 std::string datastring((char*)&data[2], datasize-2);
3364 std::istringstream is(datastring, std::ios_base::binary);
3365 u8 damage = readU8(is);
3367 if(g_settings->getBool("enable_damage"))
3369 actionstream<<player->getName()<<" damaged by "
3370 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3373 HandlePlayerHP(player, damage);
3377 SendPlayerHP(player);
3380 else if(command == TOSERVER_PASSWORD)
3383 [0] u16 TOSERVER_PASSWORD
3384 [2] u8[28] old password
3385 [30] u8[28] new password
3388 if(datasize != 2+PASSWORD_SIZE*2)
3390 /*char password[PASSWORD_SIZE];
3391 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3392 password[i] = data[2+i];
3393 password[PASSWORD_SIZE-1] = 0;*/
3395 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3403 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3405 char c = data[2+PASSWORD_SIZE+i];
3411 infostream<<"Server: Client requests a password change from "
3412 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3414 std::string playername = player->getName();
3416 if(m_authmanager.exists(playername) == false)
3418 infostream<<"Server: playername not found in authmanager"<<std::endl;
3419 // Wrong old password supplied!!
3420 SendChatMessage(peer_id, L"playername not found in authmanager");
3424 std::string checkpwd = m_authmanager.getPassword(playername);
3426 if(oldpwd != checkpwd)
3428 infostream<<"Server: invalid old password"<<std::endl;
3429 // Wrong old password supplied!!
3430 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3434 actionstream<<player->getName()<<" changes password"<<std::endl;
3436 m_authmanager.setPassword(playername, newpwd);
3438 infostream<<"Server: password change successful for "<<playername
3440 SendChatMessage(peer_id, L"Password change successful");
3442 else if(command == TOSERVER_PLAYERITEM)
3447 u16 item = readU16(&data[2]);
3448 player->wieldItem(item);
3449 SendWieldedItem(player);
3451 else if(command == TOSERVER_RESPAWN)
3456 RespawnPlayer(player);
3458 actionstream<<player->getName()<<" respawns at "
3459 <<PP(player->getPosition()/BS)<<std::endl;
3463 infostream<<"Server::ProcessData(): Ignoring "
3464 "unknown command "<<command<<std::endl;
3468 catch(SendFailedException &e)
3470 errorstream<<"Server::ProcessData(): SendFailedException: "
3476 void Server::onMapEditEvent(MapEditEvent *event)
3478 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3479 if(m_ignore_map_edit_events)
3481 MapEditEvent *e = event->clone();
3482 m_unsent_map_edit_queue.push_back(e);
3485 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3487 if(id == "current_player")
3489 assert(c->current_player);
3490 return &(c->current_player->inventory);
3494 std::string id0 = fn.next(":");
3496 if(id0 == "nodemeta")
3499 p.X = stoi(fn.next(","));
3500 p.Y = stoi(fn.next(","));
3501 p.Z = stoi(fn.next(","));
3502 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3504 return meta->getInventory();
3505 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3506 <<"no metadata found"<<std::endl;
3510 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3513 void Server::inventoryModified(InventoryContext *c, std::string id)
3515 if(id == "current_player")
3517 assert(c->current_player);
3519 UpdateCrafting(c->current_player->peer_id);
3520 SendInventory(c->current_player->peer_id);
3525 std::string id0 = fn.next(":");
3527 if(id0 == "nodemeta")
3530 p.X = stoi(fn.next(","));
3531 p.Y = stoi(fn.next(","));
3532 p.Z = stoi(fn.next(","));
3533 v3s16 blockpos = getNodeBlockPos(p);
3535 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3537 meta->inventoryModified();
3539 for(core::map<u16, RemoteClient*>::Iterator
3540 i = m_clients.getIterator();
3541 i.atEnd()==false; i++)
3543 RemoteClient *client = i.getNode()->getValue();
3544 client->SetBlockNotSent(blockpos);
3550 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3553 core::list<PlayerInfo> Server::getPlayerInfo()
3555 DSTACK(__FUNCTION_NAME);
3556 JMutexAutoLock envlock(m_env_mutex);
3557 JMutexAutoLock conlock(m_con_mutex);
3559 core::list<PlayerInfo> list;
3561 core::list<Player*> players = m_env->getPlayers();
3563 core::list<Player*>::Iterator i;
3564 for(i = players.begin();
3565 i != players.end(); i++)
3569 Player *player = *i;
3572 // Copy info from connection to info struct
3573 info.id = player->peer_id;
3574 info.address = m_con.GetPeerAddress(player->peer_id);
3575 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3577 catch(con::PeerNotFoundException &e)
3579 // Set dummy peer info
3581 info.address = Address(0,0,0,0,0);
3585 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3586 info.position = player->getPosition();
3588 list.push_back(info);
3595 void Server::peerAdded(con::Peer *peer)
3597 DSTACK(__FUNCTION_NAME);
3598 infostream<<"Server::peerAdded(): peer->id="
3599 <<peer->id<<std::endl;
3602 c.type = PEER_ADDED;
3603 c.peer_id = peer->id;
3605 m_peer_change_queue.push_back(c);
3608 void Server::deletingPeer(con::Peer *peer, bool timeout)
3610 DSTACK(__FUNCTION_NAME);
3611 infostream<<"Server::deletingPeer(): peer->id="
3612 <<peer->id<<", timeout="<<timeout<<std::endl;
3615 c.type = PEER_REMOVED;
3616 c.peer_id = peer->id;
3617 c.timeout = timeout;
3618 m_peer_change_queue.push_back(c);
3625 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3627 DSTACK(__FUNCTION_NAME);
3628 std::ostringstream os(std::ios_base::binary);
3630 writeU16(os, TOCLIENT_HP);
3634 std::string s = os.str();
3635 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3637 con.Send(peer_id, 0, data, true);
3640 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3641 const std::wstring &reason)
3643 DSTACK(__FUNCTION_NAME);
3644 std::ostringstream os(std::ios_base::binary);
3646 writeU16(os, TOCLIENT_ACCESS_DENIED);
3647 os<<serializeWideString(reason);
3650 std::string s = os.str();
3651 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3653 con.Send(peer_id, 0, data, true);
3656 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3657 bool set_camera_point_target, v3f camera_point_target)
3659 DSTACK(__FUNCTION_NAME);
3660 std::ostringstream os(std::ios_base::binary);
3662 writeU16(os, TOCLIENT_DEATHSCREEN);
3663 writeU8(os, set_camera_point_target);
3664 writeV3F1000(os, camera_point_target);
3667 std::string s = os.str();
3668 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3670 con.Send(peer_id, 0, data, true);
3673 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3674 IToolDefManager *tooldef)
3676 DSTACK(__FUNCTION_NAME);
3677 std::ostringstream os(std::ios_base::binary);
3681 u32 length of the next item
3682 serialized ToolDefManager
3684 writeU16(os, TOCLIENT_TOOLDEF);
3685 std::ostringstream tmp_os(std::ios::binary);
3686 tooldef->serialize(tmp_os);
3687 os<<serializeLongString(tmp_os.str());
3690 std::string s = os.str();
3691 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3692 <<s.size()<<std::endl;
3693 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3695 con.Send(peer_id, 0, data, true);
3698 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3699 INodeDefManager *nodedef)
3701 DSTACK(__FUNCTION_NAME);
3702 std::ostringstream os(std::ios_base::binary);
3706 u32 length of the next item
3707 serialized NodeDefManager
3709 writeU16(os, TOCLIENT_NODEDEF);
3710 std::ostringstream tmp_os(std::ios::binary);
3711 nodedef->serialize(tmp_os);
3712 os<<serializeLongString(tmp_os.str());
3715 std::string s = os.str();
3716 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3717 <<s.size()<<std::endl;
3718 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3720 con.Send(peer_id, 0, data, true);
3724 Non-static send methods
3727 void Server::SendObjectData(float dtime)
3729 DSTACK(__FUNCTION_NAME);
3731 core::map<v3s16, bool> stepped_blocks;
3733 for(core::map<u16, RemoteClient*>::Iterator
3734 i = m_clients.getIterator();
3735 i.atEnd() == false; i++)
3737 u16 peer_id = i.getNode()->getKey();
3738 RemoteClient *client = i.getNode()->getValue();
3739 assert(client->peer_id == peer_id);
3741 if(client->serialization_version == SER_FMT_VER_INVALID)
3744 client->SendObjectData(this, dtime, stepped_blocks);
3748 void Server::SendPlayerInfos()
3750 DSTACK(__FUNCTION_NAME);
3752 //JMutexAutoLock envlock(m_env_mutex);
3754 // Get connected players
3755 core::list<Player*> players = m_env->getPlayers(true);
3757 u32 player_count = players.getSize();
3758 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3760 SharedBuffer<u8> data(datasize);
3761 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3764 core::list<Player*>::Iterator i;
3765 for(i = players.begin();
3766 i != players.end(); i++)
3768 Player *player = *i;
3770 /*infostream<<"Server sending player info for player with "
3771 "peer_id="<<player->peer_id<<std::endl;*/
3773 writeU16(&data[start], player->peer_id);
3774 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3775 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3776 start += 2+PLAYERNAME_SIZE;
3779 //JMutexAutoLock conlock(m_con_mutex);
3782 m_con.SendToAll(0, data, true);
3785 void Server::SendInventory(u16 peer_id)
3787 DSTACK(__FUNCTION_NAME);
3789 Player* player = m_env->getPlayer(peer_id);
3796 std::ostringstream os;
3797 //os.imbue(std::locale("C"));
3799 player->inventory.serialize(os);
3801 std::string s = os.str();
3803 SharedBuffer<u8> data(s.size()+2);
3804 writeU16(&data[0], TOCLIENT_INVENTORY);
3805 memcpy(&data[2], s.c_str(), s.size());
3808 m_con.Send(peer_id, 0, data, true);
3811 std::string getWieldedItemString(const Player *player)
3813 const InventoryItem *item = player->getWieldItem();
3815 return std::string("");
3816 std::ostringstream os(std::ios_base::binary);
3817 item->serialize(os);
3821 void Server::SendWieldedItem(const Player* player)
3823 DSTACK(__FUNCTION_NAME);
3827 std::ostringstream os(std::ios_base::binary);
3829 writeU16(os, TOCLIENT_PLAYERITEM);
3831 writeU16(os, player->peer_id);
3832 os<<serializeString(getWieldedItemString(player));
3835 std::string s = os.str();
3836 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3838 m_con.SendToAll(0, data, true);
3841 void Server::SendPlayerItems()
3843 DSTACK(__FUNCTION_NAME);
3845 std::ostringstream os(std::ios_base::binary);
3846 core::list<Player *> players = m_env->getPlayers(true);
3848 writeU16(os, TOCLIENT_PLAYERITEM);
3849 writeU16(os, players.size());
3850 core::list<Player *>::Iterator i;
3851 for(i = players.begin(); i != players.end(); ++i)
3854 writeU16(os, p->peer_id);
3855 os<<serializeString(getWieldedItemString(p));
3859 std::string s = os.str();
3860 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3862 m_con.SendToAll(0, data, true);
3865 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3867 DSTACK(__FUNCTION_NAME);
3869 std::ostringstream os(std::ios_base::binary);
3873 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3874 os.write((char*)buf, 2);
3877 writeU16(buf, message.size());
3878 os.write((char*)buf, 2);
3881 for(u32 i=0; i<message.size(); i++)
3885 os.write((char*)buf, 2);
3889 std::string s = os.str();
3890 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3892 m_con.Send(peer_id, 0, data, true);
3895 void Server::BroadcastChatMessage(const std::wstring &message)
3897 for(core::map<u16, RemoteClient*>::Iterator
3898 i = m_clients.getIterator();
3899 i.atEnd() == false; i++)
3901 // Get client and check that it is valid
3902 RemoteClient *client = i.getNode()->getValue();
3903 assert(client->peer_id == i.getNode()->getKey());
3904 if(client->serialization_version == SER_FMT_VER_INVALID)
3907 SendChatMessage(client->peer_id, message);
3911 void Server::SendPlayerHP(Player *player)
3913 SendHP(m_con, player->peer_id, player->hp);
3916 void Server::SendMovePlayer(Player *player)
3918 DSTACK(__FUNCTION_NAME);
3919 std::ostringstream os(std::ios_base::binary);
3921 writeU16(os, TOCLIENT_MOVE_PLAYER);
3922 writeV3F1000(os, player->getPosition());
3923 writeF1000(os, player->getPitch());
3924 writeF1000(os, player->getYaw());
3927 v3f pos = player->getPosition();
3928 f32 pitch = player->getPitch();
3929 f32 yaw = player->getYaw();
3930 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3931 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3938 std::string s = os.str();
3939 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3941 m_con.Send(player->peer_id, 0, data, true);
3944 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3945 core::list<u16> *far_players, float far_d_nodes)
3947 float maxd = far_d_nodes*BS;
3948 v3f p_f = intToFloat(p, BS);
3952 SharedBuffer<u8> reply(replysize);
3953 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3954 writeS16(&reply[2], p.X);
3955 writeS16(&reply[4], p.Y);
3956 writeS16(&reply[6], p.Z);
3958 for(core::map<u16, RemoteClient*>::Iterator
3959 i = m_clients.getIterator();
3960 i.atEnd() == false; i++)
3962 // Get client and check that it is valid
3963 RemoteClient *client = i.getNode()->getValue();
3964 assert(client->peer_id == i.getNode()->getKey());
3965 if(client->serialization_version == SER_FMT_VER_INVALID)
3968 // Don't send if it's the same one
3969 if(client->peer_id == ignore_id)
3975 Player *player = m_env->getPlayer(client->peer_id);
3978 // If player is far away, only set modified blocks not sent
3979 v3f player_pos = player->getPosition();
3980 if(player_pos.getDistanceFrom(p_f) > maxd)
3982 far_players->push_back(client->peer_id);
3989 m_con.Send(client->peer_id, 0, reply, true);
3993 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3994 core::list<u16> *far_players, float far_d_nodes)
3996 float maxd = far_d_nodes*BS;
3997 v3f p_f = intToFloat(p, BS);
3999 for(core::map<u16, RemoteClient*>::Iterator
4000 i = m_clients.getIterator();
4001 i.atEnd() == false; i++)
4003 // Get client and check that it is valid
4004 RemoteClient *client = i.getNode()->getValue();
4005 assert(client->peer_id == i.getNode()->getKey());
4006 if(client->serialization_version == SER_FMT_VER_INVALID)
4009 // Don't send if it's the same one
4010 if(client->peer_id == ignore_id)
4016 Player *player = m_env->getPlayer(client->peer_id);
4019 // If player is far away, only set modified blocks not sent
4020 v3f player_pos = player->getPosition();
4021 if(player_pos.getDistanceFrom(p_f) > maxd)
4023 far_players->push_back(client->peer_id);
4030 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4031 SharedBuffer<u8> reply(replysize);
4032 writeU16(&reply[0], TOCLIENT_ADDNODE);
4033 writeS16(&reply[2], p.X);
4034 writeS16(&reply[4], p.Y);
4035 writeS16(&reply[6], p.Z);
4036 n.serialize(&reply[8], client->serialization_version);
4039 m_con.Send(client->peer_id, 0, reply, true);
4043 void Server::setBlockNotSent(v3s16 p)
4045 for(core::map<u16, RemoteClient*>::Iterator
4046 i = m_clients.getIterator();
4047 i.atEnd()==false; i++)
4049 RemoteClient *client = i.getNode()->getValue();
4050 client->SetBlockNotSent(p);
4054 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4056 DSTACK(__FUNCTION_NAME);
4058 v3s16 p = block->getPos();
4062 bool completely_air = true;
4063 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4064 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4065 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4067 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4069 completely_air = false;
4070 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4075 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4077 infostream<<"[completely air] ";
4078 infostream<<std::endl;
4082 Create a packet with the block in the right format
4085 std::ostringstream os(std::ios_base::binary);
4086 block->serialize(os, ver);
4087 std::string s = os.str();
4088 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4090 u32 replysize = 8 + blockdata.getSize();
4091 SharedBuffer<u8> reply(replysize);
4092 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4093 writeS16(&reply[2], p.X);
4094 writeS16(&reply[4], p.Y);
4095 writeS16(&reply[6], p.Z);
4096 memcpy(&reply[8], *blockdata, blockdata.getSize());
4098 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4099 <<": \tpacket size: "<<replysize<<std::endl;*/
4104 m_con.Send(peer_id, 1, reply, true);
4107 void Server::SendBlocks(float dtime)
4109 DSTACK(__FUNCTION_NAME);
4111 JMutexAutoLock envlock(m_env_mutex);
4112 JMutexAutoLock conlock(m_con_mutex);
4114 //TimeTaker timer("Server::SendBlocks");
4116 core::array<PrioritySortedBlockTransfer> queue;
4118 s32 total_sending = 0;
4121 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4123 for(core::map<u16, RemoteClient*>::Iterator
4124 i = m_clients.getIterator();
4125 i.atEnd() == false; i++)
4127 RemoteClient *client = i.getNode()->getValue();
4128 assert(client->peer_id == i.getNode()->getKey());
4130 total_sending += client->SendingCount();
4132 if(client->serialization_version == SER_FMT_VER_INVALID)
4135 client->GetNextBlocks(this, dtime, queue);
4140 // Lowest priority number comes first.
4141 // Lowest is most important.
4144 for(u32 i=0; i<queue.size(); i++)
4146 //TODO: Calculate limit dynamically
4147 if(total_sending >= g_settings->getS32
4148 ("max_simultaneous_block_sends_server_total"))
4151 PrioritySortedBlockTransfer q = queue[i];
4153 MapBlock *block = NULL;
4156 block = m_env->getMap().getBlockNoCreate(q.pos);
4158 catch(InvalidPositionException &e)
4163 RemoteClient *client = getClient(q.peer_id);
4165 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4167 client->SentBlock(q.pos);
4173 struct SendableTexture
4179 SendableTexture(const std::string &name_="", const std::string path_="",
4180 const std::string &data_=""):
4187 void Server::SendTextures(u16 peer_id)
4189 DSTACK(__FUNCTION_NAME);
4191 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4195 // Put 5kB in one bunch (this is not accurate)
4196 u32 bytes_per_bunch = 5000;
4198 core::array< core::list<SendableTexture> > texture_bunches;
4199 texture_bunches.push_back(core::list<SendableTexture>());
4201 u32 texture_size_bunch_total = 0;
4202 core::list<ModSpec> mods = getMods(m_modspaths);
4203 for(core::list<ModSpec>::Iterator i = mods.begin();
4204 i != mods.end(); i++){
4206 std::string texturepath = mod.path + DIR_DELIM + "textures";
4207 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4208 for(u32 j=0; j<dirlist.size(); j++){
4209 if(dirlist[j].dir) // Ignode dirs
4211 std::string tname = dirlist[j].name;
4212 std::string tpath = texturepath + DIR_DELIM + tname;
4214 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4215 if(fis.good() == false){
4216 errorstream<<"Server::SendTextures(): Could not open \""
4217 <<tname<<"\" for reading"<<std::endl;
4220 std::ostringstream tmp_os(std::ios_base::binary);
4224 fis.read(buf, 1024);
4225 std::streamsize len = fis.gcount();
4226 tmp_os.write(buf, len);
4227 texture_size_bunch_total += len;
4236 errorstream<<"Server::SendTextures(): Failed to read \""
4237 <<tname<<"\""<<std::endl;
4240 /*infostream<<"Server::SendTextures(): Loaded \""
4241 <<tname<<"\""<<std::endl;*/
4243 texture_bunches[texture_bunches.size()-1].push_back(
4244 SendableTexture(tname, tpath, tmp_os.str()));
4246 // Start next bunch if got enough data
4247 if(texture_size_bunch_total >= bytes_per_bunch){
4248 texture_bunches.push_back(core::list<SendableTexture>());
4249 texture_size_bunch_total = 0;
4254 /* Create and send packets */
4256 u32 num_bunches = texture_bunches.size();
4257 for(u32 i=0; i<num_bunches; i++)
4261 u16 total number of texture bunches
4262 u16 index of this bunch
4263 u32 number of textures in this bunch
4271 std::ostringstream os(std::ios_base::binary);
4273 writeU16(os, TOCLIENT_TEXTURES);
4274 writeU16(os, num_bunches);
4276 writeU32(os, texture_bunches[i].size());
4278 for(core::list<SendableTexture>::Iterator
4279 j = texture_bunches[i].begin();
4280 j != texture_bunches[i].end(); j++){
4281 os<<serializeString(j->name);
4282 os<<serializeLongString(j->data);
4286 std::string s = os.str();
4287 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4288 <<" textures="<<texture_bunches[i].size()
4289 <<" size=" <<s.size()<<std::endl;
4290 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4292 m_con.Send(peer_id, 0, data, true);
4300 void Server::HandlePlayerHP(Player *player, s16 damage)
4302 if(player->hp > damage)
4304 player->hp -= damage;
4305 SendPlayerHP(player);
4309 infostream<<"Server::HandlePlayerHP(): Player "
4310 <<player->getName()<<" dies"<<std::endl;
4314 //TODO: Throw items around
4316 // Handle players that are not connected
4317 if(player->peer_id == PEER_ID_INEXISTENT){
4318 RespawnPlayer(player);
4322 SendPlayerHP(player);
4324 RemoteClient *client = getClient(player->peer_id);
4325 if(client->net_proto_version >= 3)
4327 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4331 RespawnPlayer(player);
4336 void Server::RespawnPlayer(Player *player)
4338 v3f pos = findSpawnPos(m_env->getServerMap());
4339 player->setPosition(pos);
4341 SendMovePlayer(player);
4342 SendPlayerHP(player);
4345 void Server::UpdateCrafting(u16 peer_id)
4347 DSTACK(__FUNCTION_NAME);
4349 Player* player = m_env->getPlayer(peer_id);
4353 Calculate crafting stuff
4355 if(g_settings->getBool("creative_mode") == false)
4357 InventoryList *clist = player->inventory.getList("craft");
4358 InventoryList *rlist = player->inventory.getList("craftresult");
4360 if(rlist && rlist->getUsedSlots() == 0)
4361 player->craftresult_is_preview = true;
4363 if(rlist && player->craftresult_is_preview)
4365 rlist->clearItems();
4367 if(clist && rlist && player->craftresult_is_preview)
4369 // Get result of crafting grid
4371 std::vector<InventoryItem*> items;
4372 for(u16 i=0; i<9; i++){
4373 if(clist->getItem(i) == NULL)
4374 items.push_back(NULL);
4376 items.push_back(clist->getItem(i)->clone());
4378 CraftPointerInput cpi(3, items);
4380 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4381 //InventoryItem *result = craft_get_result(items, this);
4383 rlist->addItem(result);
4386 } // if creative_mode == false
4389 RemoteClient* Server::getClient(u16 peer_id)
4391 DSTACK(__FUNCTION_NAME);
4392 //JMutexAutoLock lock(m_con_mutex);
4393 core::map<u16, RemoteClient*>::Node *n;
4394 n = m_clients.find(peer_id);
4395 // A client should exist for all peers
4397 return n->getValue();
4400 std::wstring Server::getStatusString()
4402 std::wostringstream os(std::ios_base::binary);
4405 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4407 os<<L", uptime="<<m_uptime.get();
4408 // Information about clients
4410 for(core::map<u16, RemoteClient*>::Iterator
4411 i = m_clients.getIterator();
4412 i.atEnd() == false; i++)
4414 // Get client and check that it is valid
4415 RemoteClient *client = i.getNode()->getValue();
4416 assert(client->peer_id == i.getNode()->getKey());
4417 if(client->serialization_version == SER_FMT_VER_INVALID)
4420 Player *player = m_env->getPlayer(client->peer_id);
4421 // Get name of player
4422 std::wstring name = L"unknown";
4424 name = narrow_to_wide(player->getName());
4425 // Add name to information string
4429 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4430 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4431 if(g_settings->get("motd") != "")
4432 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4436 // Saves g_settings to configpath given at initialization
4437 void Server::saveConfig()
4439 if(m_configpath != "")
4440 g_settings->updateConfigFile(m_configpath.c_str());
4443 void Server::notifyPlayer(const char *name, const std::wstring msg)
4445 Player *player = m_env->getPlayer(name);
4448 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4451 void Server::notifyPlayers(const std::wstring msg)
4453 BroadcastChatMessage(msg);
4456 // IGameDef interface
4458 IToolDefManager* Server::getToolDefManager()
4462 INodeDefManager* Server::getNodeDefManager()
4466 ICraftDefManager* Server::getCraftDefManager()
4470 ITextureSource* Server::getTextureSource()
4474 u16 Server::allocateUnknownNodeId(const std::string &name)
4476 return m_nodedef->allocateDummy(name);
4479 IWritableToolDefManager* Server::getWritableToolDefManager()
4483 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4487 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4492 v3f findSpawnPos(ServerMap &map)
4494 //return v3f(50,50,50)*BS;
4499 nodepos = v2s16(0,0);
4504 // Try to find a good place a few times
4505 for(s32 i=0; i<1000; i++)
4508 // We're going to try to throw the player to this position
4509 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4510 -range + (myrand()%(range*2)));
4511 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4512 // Get ground height at point (fallbacks to heightmap function)
4513 s16 groundheight = map.findGroundLevel(nodepos2d);
4514 // Don't go underwater
4515 if(groundheight < WATER_LEVEL)
4517 //infostream<<"-> Underwater"<<std::endl;
4520 // Don't go to high places
4521 if(groundheight > WATER_LEVEL + 4)
4523 //infostream<<"-> Underwater"<<std::endl;
4527 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4528 bool is_good = false;
4530 for(s32 i=0; i<10; i++){
4531 v3s16 blockpos = getNodeBlockPos(nodepos);
4532 map.emergeBlock(blockpos, true);
4533 MapNode n = map.getNodeNoEx(nodepos);
4534 if(n.getContent() == CONTENT_AIR){
4545 // Found a good place
4546 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4552 return intToFloat(nodepos, BS);
4555 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4558 Try to get an existing player
4560 Player *player = m_env->getPlayer(name);
4563 // If player is already connected, cancel
4564 if(player->peer_id != 0)
4566 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4571 player->peer_id = peer_id;
4573 // Reset inventory to creative if in creative mode
4574 if(g_settings->getBool("creative_mode"))
4576 // Warning: double code below
4577 // Backup actual inventory
4578 player->inventory_backup = new Inventory();
4579 *(player->inventory_backup) = player->inventory;
4580 // Set creative inventory
4581 craft_set_creative_inventory(player, this);
4588 If player with the wanted peer_id already exists, cancel.
4590 if(m_env->getPlayer(peer_id) != NULL)
4592 infostream<<"emergePlayer(): Player with wrong name but same"
4593 " peer_id already exists"<<std::endl;
4601 // Add authentication stuff
4602 m_authmanager.add(name);
4603 m_authmanager.setPassword(name, password);
4604 m_authmanager.setPrivs(name,
4605 stringToPrivs(g_settings->get("default_privs")));
4611 infostream<<"Server: Finding spawn place for player \""
4612 <<name<<"\""<<std::endl;
4614 v3f pos = findSpawnPos(m_env->getServerMap());
4616 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4619 Add player to environment
4622 m_env->addPlayer(player);
4625 Add stuff to inventory
4628 if(g_settings->getBool("creative_mode"))
4630 // Warning: double code above
4631 // Backup actual inventory
4632 player->inventory_backup = new Inventory();
4633 *(player->inventory_backup) = player->inventory;
4634 // Set creative inventory
4635 craft_set_creative_inventory(player, this);
4637 else if(g_settings->getBool("give_initial_stuff"))
4639 craft_give_initial_stuff(player, this);
4644 } // create new player
4647 void Server::handlePeerChange(PeerChange &c)
4649 JMutexAutoLock envlock(m_env_mutex);
4650 JMutexAutoLock conlock(m_con_mutex);
4652 if(c.type == PEER_ADDED)
4659 core::map<u16, RemoteClient*>::Node *n;
4660 n = m_clients.find(c.peer_id);
4661 // The client shouldn't already exist
4665 RemoteClient *client = new RemoteClient();
4666 client->peer_id = c.peer_id;
4667 m_clients.insert(client->peer_id, client);
4670 else if(c.type == PEER_REMOVED)
4677 core::map<u16, RemoteClient*>::Node *n;
4678 n = m_clients.find(c.peer_id);
4679 // The client should exist
4683 Mark objects to be not known by the client
4685 RemoteClient *client = n->getValue();
4687 for(core::map<u16, bool>::Iterator
4688 i = client->m_known_objects.getIterator();
4689 i.atEnd()==false; i++)
4692 u16 id = i.getNode()->getKey();
4693 ServerActiveObject* obj = m_env->getActiveObject(id);
4695 if(obj && obj->m_known_by_count > 0)
4696 obj->m_known_by_count--;
4699 // Collect information about leaving in chat
4700 std::wstring message;
4702 Player *player = m_env->getPlayer(c.peer_id);
4705 std::wstring name = narrow_to_wide(player->getName());
4708 message += L" left game";
4710 message += L" (timed out)";
4716 m_env->removePlayer(c.peer_id);
4719 // Set player client disconnected
4721 Player *player = m_env->getPlayer(c.peer_id);
4723 player->peer_id = 0;
4730 std::ostringstream os(std::ios_base::binary);
4731 for(core::map<u16, RemoteClient*>::Iterator
4732 i = m_clients.getIterator();
4733 i.atEnd() == false; i++)
4735 RemoteClient *client = i.getNode()->getValue();
4736 assert(client->peer_id == i.getNode()->getKey());
4737 if(client->serialization_version == SER_FMT_VER_INVALID)
4740 Player *player = m_env->getPlayer(client->peer_id);
4743 // Get name of player
4744 os<<player->getName()<<" ";
4747 actionstream<<player->getName()<<" "
4748 <<(c.timeout?"times out.":"leaves game.")
4749 <<" List of players: "
4750 <<os.str()<<std::endl;
4755 delete m_clients[c.peer_id];
4756 m_clients.remove(c.peer_id);
4758 // Send player info to all remaining clients
4761 // Send leave chat message to all remaining clients
4762 BroadcastChatMessage(message);
4771 void Server::handlePeerChanges()
4773 while(m_peer_change_queue.size() > 0)
4775 PeerChange c = m_peer_change_queue.pop_front();
4777 infostream<<"Server: Handling peer change: "
4778 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4781 handlePeerChange(c);
4785 u64 Server::getPlayerPrivs(Player *player)
4789 std::string playername = player->getName();
4790 // Local player gets all privileges regardless of
4791 // what's set on their account.
4792 if(g_settings->get("name") == playername)
4798 return getPlayerAuthPrivs(playername);
4802 void dedicated_server_loop(Server &server, bool &kill)
4804 DSTACK(__FUNCTION_NAME);
4806 infostream<<DTIME<<std::endl;
4807 infostream<<"========================"<<std::endl;
4808 infostream<<"Running dedicated server"<<std::endl;
4809 infostream<<"========================"<<std::endl;
4810 infostream<<std::endl;
4812 IntervalLimiter m_profiler_interval;
4816 // This is kind of a hack but can be done like this
4817 // because server.step() is very light
4819 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4824 if(server.getShutdownRequested() || kill)
4826 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4833 float profiler_print_interval =
4834 g_settings->getFloat("profiler_print_interval");
4835 if(profiler_print_interval != 0)
4837 if(m_profiler_interval.step(0.030, profiler_print_interval))
4839 infostream<<"Profiler:"<<std::endl;
4840 g_profiler->print(infostream);
4841 g_profiler->clear();
4848 static int counter = 0;
4854 core::list<PlayerInfo> list = server.getPlayerInfo();
4855 core::list<PlayerInfo>::Iterator i;
4856 static u32 sum_old = 0;
4857 u32 sum = PIChecksum(list);
4860 infostream<<DTIME<<"Player info:"<<std::endl;
4861 for(i=list.begin(); i!=list.end(); i++)
4863 i->PrintLine(&infostream);