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"
36 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
38 void * ServerThread::Thread()
42 DSTACK(__FUNCTION_NAME);
44 BEGIN_DEBUG_EXCEPTION_HANDLER
49 //TimeTaker timer("AsyncRunStep() + Receive()");
52 //TimeTaker timer("AsyncRunStep()");
53 m_server->AsyncRunStep();
56 //dout_server<<"Running m_server->Receive()"<<std::endl;
59 catch(con::NoIncomingDataException &e)
62 catch(con::PeerNotFoundException &e)
64 dout_server<<"Server: PeerNotFoundException"<<std::endl;
68 END_DEBUG_EXCEPTION_HANDLER
73 void * EmergeThread::Thread()
77 DSTACK(__FUNCTION_NAME);
81 BEGIN_DEBUG_EXCEPTION_HANDLER
84 Get block info from queue, emerge them and send them
87 After queue is empty, exit.
91 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
95 SharedPtr<QueuedBlockEmerge> q(qptr);
101 Do not generate over-limit
103 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
108 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
111 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
113 //TimeTaker timer("block emerge");
116 Try to emerge it from somewhere.
118 If it is only wanted as optional, only loading from disk
123 Check if any peer wants it as non-optional. In that case it
126 Also decrement the emerge queue count in clients.
129 bool optional = true;
132 core::map<u16, u8>::Iterator i;
133 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
135 //u16 peer_id = i.getNode()->getKey();
138 u8 flags = i.getNode()->getValue();
139 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
145 /*dstream<<"EmergeThread: p="
146 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
147 <<"optional="<<optional<<std::endl;*/
149 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
151 core::map<v3s16, MapBlock*> changed_blocks;
152 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
154 MapBlock *block = NULL;
155 bool got_block = true;
156 core::map<v3s16, MapBlock*> modified_blocks;
158 bool only_from_disk = false;
161 only_from_disk = true;
163 v2s16 chunkpos = map.sector_to_chunk(p2d);
165 bool generate_chunk = false;
166 if(only_from_disk == false)
168 JMutexAutoLock envlock(m_server->m_env_mutex);
169 if(map.chunkNonVolatile(chunkpos) == false)
170 generate_chunk = true;
177 JMutexAutoLock envlock(m_server->m_env_mutex);
178 map.initChunkMake(data, chunkpos);
184 JMutexAutoLock envlock(m_server->m_env_mutex);
185 map.finishChunkMake(data, changed_blocks);
190 Fetch block from map or generate a single block
193 JMutexAutoLock envlock(m_server->m_env_mutex);
195 // Load sector if it isn't loaded
196 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
197 map.loadSectorFull(p2d);
199 block = map.getBlockNoCreateNoEx(p);
200 if(!block || block->isDummy())
208 // Get, load or create sector
209 ServerMapSector *sector =
210 (ServerMapSector*)map.createSector(p2d);
212 block = map.generateBlock(p, block, sector, changed_blocks,
213 lighting_invalidated_blocks);
220 if(block->getLightingExpired()){
221 lighting_invalidated_blocks[block->getPos()] = block;
225 // TODO: Some additional checking and lighting updating,
230 JMutexAutoLock envlock(m_server->m_env_mutex);
235 Collect a list of blocks that have been modified in
236 addition to the fetched one.
239 if(lighting_invalidated_blocks.size() > 0)
241 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
242 <<" blocks"<<std::endl;*/
244 // 50-100ms for single block generation
245 //TimeTaker timer("** EmergeThread updateLighting");
247 // Update lighting without locking the environment mutex,
248 // add modified blocks to changed blocks
249 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
252 // Add all from changed_blocks to modified_blocks
253 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
254 i.atEnd() == false; i++)
256 MapBlock *block = i.getNode()->getValue();
257 modified_blocks.insert(block->getPos(), block);
260 // If we got no block, there should be no invalidated blocks
263 assert(lighting_invalidated_blocks.size() == 0);
269 Set sent status of modified blocks on clients
272 // NOTE: Server's clients are also behind the connection mutex
273 JMutexAutoLock lock(m_server->m_con_mutex);
276 Add the originally fetched block to the modified list
280 modified_blocks.insert(p, block);
284 Set the modified blocks unsent for all the clients
287 for(core::map<u16, RemoteClient*>::Iterator
288 i = m_server->m_clients.getIterator();
289 i.atEnd() == false; i++)
291 RemoteClient *client = i.getNode()->getValue();
293 if(modified_blocks.size() > 0)
295 // Remove block from sent history
296 client->SetBlocksNotSent(modified_blocks);
302 END_DEBUG_EXCEPTION_HANDLER
307 void RemoteClient::GetNextBlocks(Server *server, float dtime,
308 core::array<PrioritySortedBlockTransfer> &dest)
310 DSTACK(__FUNCTION_NAME);
313 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
316 m_nothing_to_send_pause_timer -= dtime;
318 if(m_nothing_to_send_pause_timer >= 0)
321 m_nearest_unsent_reset_timer = 0;
325 // Won't send anything if already sending
326 if(m_blocks_sending.size() >= g_settings.getU16
327 ("max_simultaneous_block_sends_per_client"))
329 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
333 //TimeTaker timer("RemoteClient::GetNextBlocks");
335 Player *player = server->m_env.getPlayer(peer_id);
337 assert(player != NULL);
339 v3f playerpos = player->getPosition();
340 v3f playerspeed = player->getSpeed();
341 v3f playerspeeddir(0,0,0);
342 if(playerspeed.getLength() > 1.0*BS)
343 playerspeeddir = playerspeed / playerspeed.getLength();
344 // Predict to next block
345 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
347 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
349 v3s16 center = getNodeBlockPos(center_nodepos);
351 // Camera position and direction
353 playerpos + v3f(0, BS+BS/2, 0);
354 v3f camera_dir = v3f(0,0,1);
355 camera_dir.rotateYZBy(player->getPitch());
356 camera_dir.rotateXZBy(player->getYaw());
358 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
359 <<camera_dir.Z<<")"<<std::endl;*/
362 Get the starting value of the block finder radius.
365 if(m_last_center != center)
367 m_nearest_unsent_d = 0;
368 m_last_center = center;
371 /*dstream<<"m_nearest_unsent_reset_timer="
372 <<m_nearest_unsent_reset_timer<<std::endl;*/
374 // This has to be incremented only when the nothing to send pause
376 m_nearest_unsent_reset_timer += dtime;
378 // Reset periodically to avoid possible bugs or other mishaps
379 if(m_nearest_unsent_reset_timer > 10.0)
381 m_nearest_unsent_reset_timer = 0;
382 m_nearest_unsent_d = 0;
383 /*dstream<<"Resetting m_nearest_unsent_d for "
384 <<server->getPlayerName(peer_id)<<std::endl;*/
387 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
388 s16 d_start = m_nearest_unsent_d;
390 //dstream<<"d_start="<<d_start<<std::endl;
392 u16 max_simul_sends_setting = g_settings.getU16
393 ("max_simultaneous_block_sends_per_client");
394 u16 max_simul_sends_usually = max_simul_sends_setting;
397 Check the time from last addNode/removeNode.
399 Decrease send rate if player is building stuff.
401 m_time_from_building += dtime;
402 if(m_time_from_building < g_settings.getFloat(
403 "full_block_send_enable_min_time_from_building"))
405 max_simul_sends_usually
406 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
410 Number of blocks sending + number of blocks selected for sending
412 u32 num_blocks_selected = m_blocks_sending.size();
415 next time d will be continued from the d from which the nearest
416 unsent block was found this time.
418 This is because not necessarily any of the blocks found this
419 time are actually sent.
421 s32 new_nearest_unsent_d = -1;
423 s16 d_max = g_settings.getS16("max_block_send_distance");
424 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
426 // Don't loop very much at a time
427 if(d_max > d_start+1)
429 /*if(d_max_gen > d_start+2)
430 d_max_gen = d_start+2;*/
432 //dstream<<"Starting from "<<d_start<<std::endl;
434 bool sending_something = false;
436 bool no_blocks_found_for_sending = true;
438 bool queue_is_full = false;
441 for(d = d_start; d <= d_max; d++)
443 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
446 If m_nearest_unsent_d was changed by the EmergeThread
447 (it can change it to 0 through SetBlockNotSent),
449 Else update m_nearest_unsent_d
451 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
453 d = m_nearest_unsent_d;
454 last_nearest_unsent_d = m_nearest_unsent_d;
458 Get the border/face dot coordinates of a "d-radiused"
461 core::list<v3s16> list;
462 getFacePositions(list, d);
464 core::list<v3s16>::Iterator li;
465 for(li=list.begin(); li!=list.end(); li++)
467 v3s16 p = *li + center;
471 - Don't allow too many simultaneous transfers
472 - EXCEPT when the blocks are very close
474 Also, don't send blocks that are already flying.
477 // Start with the usual maximum
478 u16 max_simul_dynamic = max_simul_sends_usually;
480 // If block is very close, allow full maximum
481 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
482 max_simul_dynamic = max_simul_sends_setting;
484 // Don't select too many blocks for sending
485 if(num_blocks_selected >= max_simul_dynamic)
487 queue_is_full = true;
488 goto queue_full_break;
491 // Don't send blocks that are currently being transferred
492 if(m_blocks_sending.find(p) != NULL)
498 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
499 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
500 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
501 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
502 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
503 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
506 // If this is true, inexistent block will be made from scratch
507 bool generate = d <= d_max_gen;
510 /*// Limit the generating area vertically to 2/3
511 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
514 // Limit the send area vertically to 2/3
515 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
521 If block is far away, don't generate it unless it is
527 // Block center y in nodes
528 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
529 // Don't generate if it's very high or very low
530 if(y < -64 || y > 64)
534 v2s16 p2d_nodes_center(
538 // Get ground height in nodes
539 s16 gh = server->m_env.getServerMap().findGroundLevel(
542 // If differs a lot, don't generate
543 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
545 // Actually, don't even send it
551 //dstream<<"d="<<d<<std::endl;
554 Don't generate or send if not in sight
557 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
563 Don't send already sent blocks
566 if(m_blocks_sent.find(p) != NULL)
573 Check if map has this block
575 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
577 bool surely_not_found_on_disk = false;
578 bool block_is_invalid = false;
581 // Block is dummy if data doesn't exist.
582 // It means it has been not found from disk and not generated
585 surely_not_found_on_disk = true;
588 // Block is valid if lighting is up-to-date and data exists
589 if(block->isValid() == false)
591 block_is_invalid = true;
594 /*if(block->isFullyGenerated() == false)
596 block_is_invalid = true;
600 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
601 v2s16 chunkpos = map->sector_to_chunk(p2d);
602 if(map->chunkNonVolatile(chunkpos) == false)
603 block_is_invalid = true;
606 If block is not close, don't send it unless it is near
609 Block is near ground level if night-time mesh
610 differs from day-time mesh.
614 if(block->dayNightDiffed() == false)
621 If block has been marked to not exist on disk (dummy)
622 and generating new ones is not wanted, skip block.
624 if(generate == false && surely_not_found_on_disk == true)
631 Record the lowest d from which a block has been
632 found being not sent and possibly to exist
634 if(no_blocks_found_for_sending)
637 new_nearest_unsent_d = d;
640 no_blocks_found_for_sending = false;
643 Add inexistent block to emerge queue.
645 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
647 //TODO: Get value from somewhere
648 // Allow only one block in emerge queue
649 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
650 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
652 //dstream<<"Adding block to emerge queue"<<std::endl;
654 // Add it to the emerge queue and trigger the thread
657 if(generate == false)
658 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
660 server->m_emerge_queue.addBlock(peer_id, p, flags);
661 server->m_emergethread.trigger();
669 Add block to send queue
672 PrioritySortedBlockTransfer q((float)d, p, peer_id);
676 num_blocks_selected += 1;
677 sending_something = true;
682 //dstream<<"Stopped at "<<d<<std::endl;
684 if(no_blocks_found_for_sending)
686 if(queue_is_full == false)
687 new_nearest_unsent_d = d;
690 if(new_nearest_unsent_d != -1)
691 m_nearest_unsent_d = new_nearest_unsent_d;
693 if(sending_something == false)
695 m_nothing_to_send_counter++;
696 if((s16)m_nothing_to_send_counter >=
697 g_settings.getS16("max_block_send_distance"))
699 // Pause time in seconds
700 m_nothing_to_send_pause_timer = 1.0;
701 /*dstream<<"nothing to send to "
702 <<server->getPlayerName(peer_id)
703 <<" (d="<<d<<")"<<std::endl;*/
708 m_nothing_to_send_counter = 0;
711 /*timer_result = timer.stop(true);
712 if(timer_result != 0)
713 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
716 void RemoteClient::SendObjectData(
719 core::map<v3s16, bool> &stepped_blocks
722 DSTACK(__FUNCTION_NAME);
724 // Can't send anything without knowing version
725 if(serialization_version == SER_FMT_VER_INVALID)
727 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
733 Send a TOCLIENT_OBJECTDATA packet.
737 u16 number of player positions
748 std::ostringstream os(std::ios_base::binary);
752 writeU16(buf, TOCLIENT_OBJECTDATA);
753 os.write((char*)buf, 2);
756 Get and write player data
759 // Get connected players
760 core::list<Player*> players = server->m_env.getPlayers(true);
762 // Write player count
763 u16 playercount = players.size();
764 writeU16(buf, playercount);
765 os.write((char*)buf, 2);
767 core::list<Player*>::Iterator i;
768 for(i = players.begin();
769 i != players.end(); i++)
773 v3f pf = player->getPosition();
774 v3f sf = player->getSpeed();
776 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
777 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
778 s32 pitch_i (player->getPitch() * 100);
779 s32 yaw_i (player->getYaw() * 100);
781 writeU16(buf, player->peer_id);
782 os.write((char*)buf, 2);
783 writeV3S32(buf, position_i);
784 os.write((char*)buf, 12);
785 writeV3S32(buf, speed_i);
786 os.write((char*)buf, 12);
787 writeS32(buf, pitch_i);
788 os.write((char*)buf, 4);
789 writeS32(buf, yaw_i);
790 os.write((char*)buf, 4);
794 Get and write object data
800 For making players to be able to build to their nearby
801 environment (building is not possible on blocks that are not
804 - Add blocks to emerge queue if they are not found
806 SUGGESTION: These could be ignored from the backside of the player
809 Player *player = server->m_env.getPlayer(peer_id);
813 v3f playerpos = player->getPosition();
814 v3f playerspeed = player->getSpeed();
816 v3s16 center_nodepos = floatToInt(playerpos, BS);
817 v3s16 center = getNodeBlockPos(center_nodepos);
819 s16 d_max = g_settings.getS16("active_object_range");
821 // Number of blocks whose objects were written to bos
824 std::ostringstream bos(std::ios_base::binary);
826 for(s16 d = 0; d <= d_max; d++)
828 core::list<v3s16> list;
829 getFacePositions(list, d);
831 core::list<v3s16>::Iterator li;
832 for(li=list.begin(); li!=list.end(); li++)
834 v3s16 p = *li + center;
837 Ignore blocks that haven't been sent to the client
840 if(m_blocks_sent.find(p) == NULL)
844 // Try stepping block and add it to a send queue
849 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
852 Step block if not in stepped_blocks and add to stepped_blocks.
854 if(stepped_blocks.find(p) == NULL)
856 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
857 stepped_blocks.insert(p, true);
858 block->setChangedFlag();
861 // Skip block if there are no objects
862 if(block->getObjectCount() == 0)
871 bos.write((char*)buf, 6);
874 block->serializeObjects(bos, serialization_version);
879 Stop collecting objects if data is already too big
881 // Sum of player and object data sizes
882 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
883 // break out if data too big
884 if(sum > MAX_OBJECTDATA_SIZE)
886 goto skip_subsequent;
890 catch(InvalidPositionException &e)
893 // Add it to the emerge queue and trigger the thread.
894 // Fetch the block only if it is on disk.
896 // Grab and increment counter
897 /*SharedPtr<JMutexAutoLock> lock
898 (m_num_blocks_in_emerge_queue.getLock());
899 m_num_blocks_in_emerge_queue.m_value++;*/
901 // Add to queue as an anonymous fetch from disk
902 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
903 server->m_emerge_queue.addBlock(0, p, flags);
904 server->m_emergethread.trigger();
912 writeU16(buf, blockcount);
913 os.write((char*)buf, 2);
915 // Write block objects
922 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
925 std::string s = os.str();
926 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
927 // Send as unreliable
928 server->m_con.Send(peer_id, 0, data, false);
931 void RemoteClient::GotBlock(v3s16 p)
933 if(m_blocks_sending.find(p) != NULL)
934 m_blocks_sending.remove(p);
937 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
938 " m_blocks_sending"<<std::endl;*/
939 m_excess_gotblocks++;
941 m_blocks_sent.insert(p, true);
944 void RemoteClient::SentBlock(v3s16 p)
946 if(m_blocks_sending.find(p) == NULL)
947 m_blocks_sending.insert(p, 0.0);
949 dstream<<"RemoteClient::SentBlock(): Sent block"
950 " already in m_blocks_sending"<<std::endl;
953 void RemoteClient::SetBlockNotSent(v3s16 p)
955 m_nearest_unsent_d = 0;
957 if(m_blocks_sending.find(p) != NULL)
958 m_blocks_sending.remove(p);
959 if(m_blocks_sent.find(p) != NULL)
960 m_blocks_sent.remove(p);
963 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
965 m_nearest_unsent_d = 0;
967 for(core::map<v3s16, MapBlock*>::Iterator
968 i = blocks.getIterator();
969 i.atEnd()==false; i++)
971 v3s16 p = i.getNode()->getKey();
973 if(m_blocks_sending.find(p) != NULL)
974 m_blocks_sending.remove(p);
975 if(m_blocks_sent.find(p) != NULL)
976 m_blocks_sent.remove(p);
984 PlayerInfo::PlayerInfo()
990 void PlayerInfo::PrintLine(std::ostream *s)
993 (*s)<<"\""<<name<<"\" ("
994 <<(position.X/10)<<","<<(position.Y/10)
995 <<","<<(position.Z/10)<<") ";
997 (*s)<<" avg_rtt="<<avg_rtt;
1001 u32 PIChecksum(core::list<PlayerInfo> &l)
1003 core::list<PlayerInfo>::Iterator i;
1006 for(i=l.begin(); i!=l.end(); i++)
1008 checksum += a * (i->id+1);
1009 checksum ^= 0x435aafcd;
1020 std::string mapsavedir
1022 m_env(new ServerMap(mapsavedir), this),
1023 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1024 m_authmanager(mapsavedir+"/auth.txt"),
1026 m_emergethread(this),
1028 m_time_of_day_send_timer(0),
1030 m_mapsavedir(mapsavedir),
1031 m_shutdown_requested(false),
1032 m_ignore_map_edit_events(false),
1033 m_ignore_map_edit_events_peer_id(0)
1035 m_liquid_transform_timer = 0.0;
1036 m_print_info_timer = 0.0;
1037 m_objectdata_timer = 0.0;
1038 m_emergethread_trigger_timer = 0.0;
1039 m_savemap_timer = 0.0;
1043 m_step_dtime_mutex.Init();
1046 // Register us to receive map edit events
1047 m_env.getMap().addEventReceiver(this);
1049 // If file exists, load environment metadata
1050 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1052 dstream<<"Server: Loading environment metadata"<<std::endl;
1053 m_env.loadMeta(m_mapsavedir);
1057 dstream<<"Server: Loading players"<<std::endl;
1058 m_env.deSerializePlayers(m_mapsavedir);
1063 dstream<<"Server::~Server()"<<std::endl;
1066 Send shutdown message
1069 JMutexAutoLock conlock(m_con_mutex);
1071 std::wstring line = L"*** Server shutting down";
1074 Send the message to clients
1076 for(core::map<u16, RemoteClient*>::Iterator
1077 i = m_clients.getIterator();
1078 i.atEnd() == false; i++)
1080 // Get client and check that it is valid
1081 RemoteClient *client = i.getNode()->getValue();
1082 assert(client->peer_id == i.getNode()->getKey());
1083 if(client->serialization_version == SER_FMT_VER_INVALID)
1087 SendChatMessage(client->peer_id, line);
1089 catch(con::PeerNotFoundException &e)
1097 dstream<<"Server: Saving players"<<std::endl;
1098 m_env.serializePlayers(m_mapsavedir);
1101 Save environment metadata
1103 dstream<<"Server: Saving environment metadata"<<std::endl;
1104 m_env.saveMeta(m_mapsavedir);
1115 JMutexAutoLock clientslock(m_con_mutex);
1117 for(core::map<u16, RemoteClient*>::Iterator
1118 i = m_clients.getIterator();
1119 i.atEnd() == false; i++)
1122 // NOTE: These are removed by env destructor
1124 u16 peer_id = i.getNode()->getKey();
1125 JMutexAutoLock envlock(m_env_mutex);
1126 m_env.removePlayer(peer_id);
1130 delete i.getNode()->getValue();
1135 void Server::start(unsigned short port)
1137 DSTACK(__FUNCTION_NAME);
1138 // Stop thread if already running
1141 // Initialize connection
1142 m_con.setTimeoutMs(30);
1146 m_thread.setRun(true);
1149 dout_server<<"Server: Started on port "<<port<<std::endl;
1154 DSTACK(__FUNCTION_NAME);
1156 // Stop threads (set run=false first so both start stopping)
1157 m_thread.setRun(false);
1158 m_emergethread.setRun(false);
1160 m_emergethread.stop();
1162 dout_server<<"Server: Threads stopped"<<std::endl;
1165 void Server::step(float dtime)
1167 DSTACK(__FUNCTION_NAME);
1172 JMutexAutoLock lock(m_step_dtime_mutex);
1173 m_step_dtime += dtime;
1177 void Server::AsyncRunStep()
1179 DSTACK(__FUNCTION_NAME);
1183 JMutexAutoLock lock1(m_step_dtime_mutex);
1184 dtime = m_step_dtime;
1188 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1189 "blocks to clients");
1190 // Send blocks to clients
1197 //dstream<<"Server steps "<<dtime<<std::endl;
1198 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1201 JMutexAutoLock lock1(m_step_dtime_mutex);
1202 m_step_dtime -= dtime;
1209 m_uptime.set(m_uptime.get() + dtime);
1213 Update m_time_of_day and overall game time
1216 JMutexAutoLock envlock(m_env_mutex);
1218 m_time_counter += dtime;
1219 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1220 u32 units = (u32)(m_time_counter*speed);
1221 m_time_counter -= (f32)units / speed;
1223 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1225 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1228 Send to clients at constant intervals
1231 m_time_of_day_send_timer -= dtime;
1232 if(m_time_of_day_send_timer < 0.0)
1234 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1236 //JMutexAutoLock envlock(m_env_mutex);
1237 JMutexAutoLock conlock(m_con_mutex);
1239 for(core::map<u16, RemoteClient*>::Iterator
1240 i = m_clients.getIterator();
1241 i.atEnd() == false; i++)
1243 RemoteClient *client = i.getNode()->getValue();
1244 //Player *player = m_env.getPlayer(client->peer_id);
1246 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1247 m_env.getTimeOfDay());
1249 m_con.Send(client->peer_id, 0, data, true);
1255 // Process connection's timeouts
1256 JMutexAutoLock lock2(m_con_mutex);
1257 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1258 m_con.RunTimeouts(dtime);
1262 // This has to be called so that the client list gets synced
1263 // with the peer list of the connection
1264 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1265 handlePeerChanges();
1270 // This also runs Map's timers
1271 JMutexAutoLock lock(m_env_mutex);
1272 ScopeProfiler sp(&g_profiler, "Server: environment step");
1283 m_liquid_transform_timer += dtime;
1284 if(m_liquid_transform_timer >= 1.00)
1286 m_liquid_transform_timer -= 1.00;
1288 JMutexAutoLock lock(m_env_mutex);
1290 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1292 core::map<v3s16, MapBlock*> modified_blocks;
1293 m_env.getMap().transformLiquids(modified_blocks);
1298 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1299 ServerMap &map = ((ServerMap&)m_env.getMap());
1300 map.updateLighting(modified_blocks, lighting_modified_blocks);
1302 // Add blocks modified by lighting to modified_blocks
1303 for(core::map<v3s16, MapBlock*>::Iterator
1304 i = lighting_modified_blocks.getIterator();
1305 i.atEnd() == false; i++)
1307 MapBlock *block = i.getNode()->getValue();
1308 modified_blocks.insert(block->getPos(), block);
1312 Set the modified blocks unsent for all the clients
1315 JMutexAutoLock lock2(m_con_mutex);
1317 for(core::map<u16, RemoteClient*>::Iterator
1318 i = m_clients.getIterator();
1319 i.atEnd() == false; i++)
1321 RemoteClient *client = i.getNode()->getValue();
1323 if(modified_blocks.size() > 0)
1325 // Remove block from sent history
1326 client->SetBlocksNotSent(modified_blocks);
1331 // Periodically print some info
1333 float &counter = m_print_info_timer;
1339 JMutexAutoLock lock2(m_con_mutex);
1341 for(core::map<u16, RemoteClient*>::Iterator
1342 i = m_clients.getIterator();
1343 i.atEnd() == false; i++)
1345 //u16 peer_id = i.getNode()->getKey();
1346 RemoteClient *client = i.getNode()->getValue();
1347 Player *player = m_env.getPlayer(client->peer_id);
1350 std::cout<<player->getName()<<"\t";
1351 client->PrintInfo(std::cout);
1356 //if(g_settings.getBool("enable_experimental"))
1360 Check added and deleted active objects
1363 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1364 JMutexAutoLock envlock(m_env_mutex);
1365 JMutexAutoLock conlock(m_con_mutex);
1367 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1369 // Radius inside which objects are active
1372 for(core::map<u16, RemoteClient*>::Iterator
1373 i = m_clients.getIterator();
1374 i.atEnd() == false; i++)
1376 RemoteClient *client = i.getNode()->getValue();
1377 Player *player = m_env.getPlayer(client->peer_id);
1380 // This can happen if the client timeouts somehow
1381 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1383 <<" has no associated player"<<std::endl;*/
1386 v3s16 pos = floatToInt(player->getPosition(), BS);
1388 core::map<u16, bool> removed_objects;
1389 core::map<u16, bool> added_objects;
1390 m_env.getRemovedActiveObjects(pos, radius,
1391 client->m_known_objects, removed_objects);
1392 m_env.getAddedActiveObjects(pos, radius,
1393 client->m_known_objects, added_objects);
1395 // Ignore if nothing happened
1396 if(removed_objects.size() == 0 && added_objects.size() == 0)
1398 //dstream<<"INFO: active objects: none changed"<<std::endl;
1402 std::string data_buffer;
1406 // Handle removed objects
1407 writeU16((u8*)buf, removed_objects.size());
1408 data_buffer.append(buf, 2);
1409 for(core::map<u16, bool>::Iterator
1410 i = removed_objects.getIterator();
1411 i.atEnd()==false; i++)
1414 u16 id = i.getNode()->getKey();
1415 ServerActiveObject* obj = m_env.getActiveObject(id);
1417 // Add to data buffer for sending
1418 writeU16((u8*)buf, i.getNode()->getKey());
1419 data_buffer.append(buf, 2);
1421 // Remove from known objects
1422 client->m_known_objects.remove(i.getNode()->getKey());
1424 if(obj && obj->m_known_by_count > 0)
1425 obj->m_known_by_count--;
1428 // Handle added objects
1429 writeU16((u8*)buf, added_objects.size());
1430 data_buffer.append(buf, 2);
1431 for(core::map<u16, bool>::Iterator
1432 i = added_objects.getIterator();
1433 i.atEnd()==false; i++)
1436 u16 id = i.getNode()->getKey();
1437 ServerActiveObject* obj = m_env.getActiveObject(id);
1440 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1442 dstream<<"WARNING: "<<__FUNCTION_NAME
1443 <<": NULL object"<<std::endl;
1445 type = obj->getType();
1447 // Add to data buffer for sending
1448 writeU16((u8*)buf, id);
1449 data_buffer.append(buf, 2);
1450 writeU8((u8*)buf, type);
1451 data_buffer.append(buf, 1);
1454 data_buffer.append(serializeLongString(
1455 obj->getClientInitializationData()));
1457 data_buffer.append(serializeLongString(""));
1459 // Add to known objects
1460 client->m_known_objects.insert(i.getNode()->getKey(), false);
1463 obj->m_known_by_count++;
1467 SharedBuffer<u8> reply(2 + data_buffer.size());
1468 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1469 memcpy((char*)&reply[2], data_buffer.c_str(),
1470 data_buffer.size());
1472 m_con.Send(client->peer_id, 0, reply, true);
1474 dstream<<"INFO: Server: Sent object remove/add: "
1475 <<removed_objects.size()<<" removed, "
1476 <<added_objects.size()<<" added, "
1477 <<"packet size is "<<reply.getSize()<<std::endl;
1482 Collect a list of all the objects known by the clients
1483 and report it back to the environment.
1486 core::map<u16, bool> all_known_objects;
1488 for(core::map<u16, RemoteClient*>::Iterator
1489 i = m_clients.getIterator();
1490 i.atEnd() == false; i++)
1492 RemoteClient *client = i.getNode()->getValue();
1493 // Go through all known objects of client
1494 for(core::map<u16, bool>::Iterator
1495 i = client->m_known_objects.getIterator();
1496 i.atEnd()==false; i++)
1498 u16 id = i.getNode()->getKey();
1499 all_known_objects[id] = true;
1503 m_env.setKnownActiveObjects(whatever);
1509 Send object messages
1512 JMutexAutoLock envlock(m_env_mutex);
1513 JMutexAutoLock conlock(m_con_mutex);
1515 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1518 // Value = data sent by object
1519 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1521 // Get active object messages from environment
1524 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1528 core::list<ActiveObjectMessage>* message_list = NULL;
1529 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1530 n = buffered_messages.find(aom.id);
1533 message_list = new core::list<ActiveObjectMessage>;
1534 buffered_messages.insert(aom.id, message_list);
1538 message_list = n->getValue();
1540 message_list->push_back(aom);
1543 // Route data to every client
1544 for(core::map<u16, RemoteClient*>::Iterator
1545 i = m_clients.getIterator();
1546 i.atEnd()==false; i++)
1548 RemoteClient *client = i.getNode()->getValue();
1549 std::string reliable_data;
1550 std::string unreliable_data;
1551 // Go through all objects in message buffer
1552 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1553 j = buffered_messages.getIterator();
1554 j.atEnd()==false; j++)
1556 // If object is not known by client, skip it
1557 u16 id = j.getNode()->getKey();
1558 if(client->m_known_objects.find(id) == NULL)
1560 // Get message list of object
1561 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1562 // Go through every message
1563 for(core::list<ActiveObjectMessage>::Iterator
1564 k = list->begin(); k != list->end(); k++)
1566 // Compose the full new data with header
1567 ActiveObjectMessage aom = *k;
1568 std::string new_data;
1571 writeU16((u8*)&buf[0], aom.id);
1572 new_data.append(buf, 2);
1574 new_data += serializeString(aom.datastring);
1575 // Add data to buffer
1577 reliable_data += new_data;
1579 unreliable_data += new_data;
1583 reliable_data and unreliable_data are now ready.
1586 if(reliable_data.size() > 0)
1588 SharedBuffer<u8> reply(2 + reliable_data.size());
1589 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1590 memcpy((char*)&reply[2], reliable_data.c_str(),
1591 reliable_data.size());
1593 m_con.Send(client->peer_id, 0, reply, true);
1595 if(unreliable_data.size() > 0)
1597 SharedBuffer<u8> reply(2 + unreliable_data.size());
1598 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1599 memcpy((char*)&reply[2], unreliable_data.c_str(),
1600 unreliable_data.size());
1601 // Send as unreliable
1602 m_con.Send(client->peer_id, 0, reply, false);
1605 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1607 dstream<<"INFO: Server: Size of object message data: "
1608 <<"reliable: "<<reliable_data.size()
1609 <<", unreliable: "<<unreliable_data.size()
1614 // Clear buffered_messages
1615 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1616 i = buffered_messages.getIterator();
1617 i.atEnd()==false; i++)
1619 delete i.getNode()->getValue();
1623 } // enable_experimental
1626 Send queued-for-sending map edit events.
1629 while(m_unsent_map_edit_queue.size() != 0)
1631 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1633 if(event->type == MEET_ADDNODE)
1635 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1636 sendAddNode(event->p, event->n, event->already_known_by_peer);
1638 else if(event->type == MEET_REMOVENODE)
1640 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1641 sendRemoveNode(event->p, event->already_known_by_peer);
1643 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1645 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1646 setBlockNotSent(event->p);
1648 else if(event->type == MEET_OTHER)
1650 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1655 dstream<<"WARNING: Server: Unknown MapEditEvent "
1656 <<((u32)event->type)<<std::endl;
1664 Send object positions
1665 TODO: Get rid of MapBlockObjects
1668 float &counter = m_objectdata_timer;
1670 if(counter >= g_settings.getFloat("objectdata_interval"))
1672 JMutexAutoLock lock1(m_env_mutex);
1673 JMutexAutoLock lock2(m_con_mutex);
1675 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1677 SendObjectData(counter);
1685 TODO: Move to ServerEnvironment and utilize active block stuff
1688 //TimeTaker timer("Step node metadata");
1690 JMutexAutoLock envlock(m_env_mutex);
1691 JMutexAutoLock conlock(m_con_mutex);
1693 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1695 core::map<v3s16, MapBlock*> changed_blocks;
1696 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1698 // Use setBlockNotSent
1700 for(core::map<v3s16, MapBlock*>::Iterator
1701 i = changed_blocks.getIterator();
1702 i.atEnd() == false; i++)
1704 MapBlock *block = i.getNode()->getValue();
1706 for(core::map<u16, RemoteClient*>::Iterator
1707 i = m_clients.getIterator();
1708 i.atEnd()==false; i++)
1710 RemoteClient *client = i.getNode()->getValue();
1711 client->SetBlockNotSent(block->getPos());
1717 Trigger emergethread (it somehow gets to a non-triggered but
1718 bysy state sometimes)
1721 float &counter = m_emergethread_trigger_timer;
1727 m_emergethread.trigger();
1731 // Save map, players and auth stuff
1733 float &counter = m_savemap_timer;
1735 if(counter >= g_settings.getFloat("server_map_save_interval"))
1739 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1742 if(m_authmanager.isModified())
1743 m_authmanager.save();
1746 JMutexAutoLock lock(m_env_mutex);
1747 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1749 // Save only changed parts
1750 m_env.getMap().save(true);
1752 // Delete unused sectors
1753 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1754 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1755 if(deleted_count > 0)
1757 dout_server<<"Server: Unloaded "<<deleted_count
1758 <<" sectors from memory"<<std::endl;
1762 m_env.serializePlayers(m_mapsavedir);
1764 // Save environment metadata
1765 m_env.saveMeta(m_mapsavedir);
1771 void Server::Receive()
1773 DSTACK(__FUNCTION_NAME);
1774 u32 data_maxsize = 10000;
1775 Buffer<u8> data(data_maxsize);
1780 JMutexAutoLock conlock(m_con_mutex);
1781 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1784 // This has to be called so that the client list gets synced
1785 // with the peer list of the connection
1786 handlePeerChanges();
1788 ProcessData(*data, datasize, peer_id);
1790 catch(con::InvalidIncomingDataException &e)
1792 derr_server<<"Server::Receive(): "
1793 "InvalidIncomingDataException: what()="
1794 <<e.what()<<std::endl;
1796 catch(con::PeerNotFoundException &e)
1798 //NOTE: This is not needed anymore
1800 // The peer has been disconnected.
1801 // Find the associated player and remove it.
1803 /*JMutexAutoLock envlock(m_env_mutex);
1805 dout_server<<"ServerThread: peer_id="<<peer_id
1806 <<" has apparently closed connection. "
1807 <<"Removing player."<<std::endl;
1809 m_env.removePlayer(peer_id);*/
1813 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1815 DSTACK(__FUNCTION_NAME);
1816 // Environment is locked first.
1817 JMutexAutoLock envlock(m_env_mutex);
1818 JMutexAutoLock conlock(m_con_mutex);
1822 peer = m_con.GetPeer(peer_id);
1824 catch(con::PeerNotFoundException &e)
1826 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1827 <<peer_id<<" not found"<<std::endl;
1831 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1839 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1841 if(command == TOSERVER_INIT)
1843 // [0] u16 TOSERVER_INIT
1844 // [2] u8 SER_FMT_VER_HIGHEST
1845 // [3] u8[20] player_name
1846 // [23] u8[28] password <--- can be sent without this, from old versions
1848 if(datasize < 2+1+PLAYERNAME_SIZE)
1851 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1852 <<peer->id<<std::endl;
1854 // First byte after command is maximum supported
1855 // serialization version
1856 u8 client_max = data[2];
1857 u8 our_max = SER_FMT_VER_HIGHEST;
1858 // Use the highest version supported by both
1859 u8 deployed = core::min_(client_max, our_max);
1860 // If it's lower than the lowest supported, give up.
1861 if(deployed < SER_FMT_VER_LOWEST)
1862 deployed = SER_FMT_VER_INVALID;
1864 //peer->serialization_version = deployed;
1865 getClient(peer->id)->pending_serialization_version = deployed;
1867 if(deployed == SER_FMT_VER_INVALID)
1869 derr_server<<DTIME<<"Server: Cannot negotiate "
1870 "serialization version with peer "
1871 <<peer_id<<std::endl;
1880 char playername[PLAYERNAME_SIZE];
1881 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1883 playername[i] = data[3+i];
1885 playername[PLAYERNAME_SIZE-1] = 0;
1887 if(playername[0]=='\0')
1889 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1890 SendAccessDenied(m_con, peer_id,
1895 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1897 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1898 SendAccessDenied(m_con, peer_id,
1899 L"Name contains unallowed characters");
1904 char password[PASSWORD_SIZE];
1905 if(datasize == 2+1+PLAYERNAME_SIZE)
1907 // old version - assume blank password
1912 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1914 password[i] = data[23+i];
1916 password[PASSWORD_SIZE-1] = 0;
1919 std::string checkpwd;
1920 if(m_authmanager.exists(playername))
1922 checkpwd = m_authmanager.getPassword(playername);
1926 checkpwd = g_settings.get("default_password");
1929 if(password != checkpwd && checkpwd != "")
1931 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1932 <<": supplied invalid password for "
1933 <<playername<<std::endl;
1934 SendAccessDenied(m_con, peer_id, L"Invalid password");
1938 // Add player to auth manager
1939 if(m_authmanager.exists(playername) == false)
1941 derr_server<<DTIME<<"Server: adding player "<<playername
1942 <<" to auth manager"<<std::endl;
1943 m_authmanager.add(playername);
1944 m_authmanager.setPassword(playername, checkpwd);
1945 m_authmanager.setPrivs(playername,
1946 stringToPrivs(g_settings.get("default_privs")));
1947 m_authmanager.save();
1951 Player *player = emergePlayer(playername, password, peer_id);
1955 // DEBUG: Test serialization
1956 std::ostringstream test_os;
1957 player->serialize(test_os);
1958 dstream<<"Player serialization test: \""<<test_os.str()
1960 std::istringstream test_is(test_os.str());
1961 player->deSerialize(test_is);
1964 // If failed, cancel
1967 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1968 <<": failed to emerge player"<<std::endl;
1973 // If a client is already connected to the player, cancel
1974 if(player->peer_id != 0)
1976 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1977 <<" tried to connect to "
1978 "an already connected player (peer_id="
1979 <<player->peer_id<<")"<<std::endl;
1982 // Set client of player
1983 player->peer_id = peer_id;
1986 // Check if player doesn't exist
1988 throw con::InvalidIncomingDataException
1989 ("Server::ProcessData(): INIT: Player doesn't exist");
1991 /*// update name if it was supplied
1992 if(datasize >= 20+3)
1995 player->updateName((const char*)&data[3]);
1999 Answer with a TOCLIENT_INIT
2002 SharedBuffer<u8> reply(2+1+6+8);
2003 writeU16(&reply[0], TOCLIENT_INIT);
2004 writeU8(&reply[2], deployed);
2005 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2006 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2009 m_con.Send(peer_id, 0, reply, true);
2013 Send complete position information
2015 SendMovePlayer(player);
2020 if(command == TOSERVER_INIT2)
2022 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2023 <<peer->id<<std::endl;
2026 getClient(peer->id)->serialization_version
2027 = getClient(peer->id)->pending_serialization_version;
2030 Send some initialization data
2033 // Send player info to all players
2036 // Send inventory to player
2037 UpdateCrafting(peer->id);
2038 SendInventory(peer->id);
2042 Player *player = m_env.getPlayer(peer_id);
2043 SendPlayerHP(player);
2048 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2049 m_env.getTimeOfDay());
2050 m_con.Send(peer->id, 0, data, true);
2053 // Send information about server to player in chat
2054 SendChatMessage(peer_id, getStatusString());
2056 // Send information about joining in chat
2058 std::wstring name = L"unknown";
2059 Player *player = m_env.getPlayer(peer_id);
2061 name = narrow_to_wide(player->getName());
2063 std::wstring message;
2066 message += L" joined game";
2067 BroadcastChatMessage(message);
2073 if(peer_ser_ver == SER_FMT_VER_INVALID)
2075 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2076 " serialization format invalid or not initialized."
2077 " Skipping incoming command="<<command<<std::endl;
2081 Player *player = m_env.getPlayer(peer_id);
2084 derr_server<<"Server::ProcessData(): Cancelling: "
2085 "No player for peer_id="<<peer_id
2089 if(command == TOSERVER_PLAYERPOS)
2091 if(datasize < 2+12+12+4+4)
2095 v3s32 ps = readV3S32(&data[start+2]);
2096 v3s32 ss = readV3S32(&data[start+2+12]);
2097 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2098 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2099 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2100 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2101 pitch = wrapDegrees(pitch);
2102 yaw = wrapDegrees(yaw);
2103 player->setPosition(position);
2104 player->setSpeed(speed);
2105 player->setPitch(pitch);
2106 player->setYaw(yaw);
2108 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2109 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2110 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2112 else if(command == TOSERVER_GOTBLOCKS)
2125 u16 count = data[2];
2126 for(u16 i=0; i<count; i++)
2128 if((s16)datasize < 2+1+(i+1)*6)
2129 throw con::InvalidIncomingDataException
2130 ("GOTBLOCKS length is too short");
2131 v3s16 p = readV3S16(&data[2+1+i*6]);
2132 /*dstream<<"Server: GOTBLOCKS ("
2133 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2134 RemoteClient *client = getClient(peer_id);
2135 client->GotBlock(p);
2138 else if(command == TOSERVER_DELETEDBLOCKS)
2151 u16 count = data[2];
2152 for(u16 i=0; i<count; i++)
2154 if((s16)datasize < 2+1+(i+1)*6)
2155 throw con::InvalidIncomingDataException
2156 ("DELETEDBLOCKS length is too short");
2157 v3s16 p = readV3S16(&data[2+1+i*6]);
2158 /*dstream<<"Server: DELETEDBLOCKS ("
2159 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2160 RemoteClient *client = getClient(peer_id);
2161 client->SetBlockNotSent(p);
2164 else if(command == TOSERVER_CLICK_OBJECT)
2169 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2174 [2] u8 button (0=left, 1=right)
2179 u8 button = readU8(&data[2]);
2181 p.X = readS16(&data[3]);
2182 p.Y = readS16(&data[5]);
2183 p.Z = readS16(&data[7]);
2184 s16 id = readS16(&data[9]);
2185 //u16 item_i = readU16(&data[11]);
2187 MapBlock *block = NULL;
2190 block = m_env.getMap().getBlockNoCreate(p);
2192 catch(InvalidPositionException &e)
2194 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2198 MapBlockObject *obj = block->getObject(id);
2202 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2206 //TODO: Check that object is reasonably close
2211 InventoryList *ilist = player->inventory.getList("main");
2212 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2215 // Skip if inventory has no free space
2216 if(ilist->getUsedSlots() == ilist->getSize())
2218 dout_server<<"Player inventory has no free space"<<std::endl;
2223 Create the inventory item
2225 InventoryItem *item = NULL;
2226 // If it is an item-object, take the item from it
2227 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2229 item = ((ItemObject*)obj)->createInventoryItem();
2231 // Else create an item of the object
2234 item = new MapBlockObjectItem
2235 (obj->getInventoryString());
2238 // Add to inventory and send inventory
2239 ilist->addItem(item);
2240 UpdateCrafting(player->peer_id);
2241 SendInventory(player->peer_id);
2244 // Remove from block
2245 block->removeObject(id);
2248 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2253 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2259 [2] u8 button (0=left, 1=right)
2263 u8 button = readU8(&data[2]);
2264 u16 id = readS16(&data[3]);
2265 u16 item_i = readU16(&data[11]);
2267 ServerActiveObject *obj = m_env.getActiveObject(id);
2271 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2276 //TODO: Check that object is reasonably close
2278 // Left click, pick object up (usually)
2281 InventoryList *ilist = player->inventory.getList("main");
2282 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2285 // Skip if inventory has no free space
2286 if(ilist->getUsedSlots() == ilist->getSize())
2288 dout_server<<"Player inventory has no free space"<<std::endl;
2292 // Skip if object has been removed
2297 Create the inventory item
2299 InventoryItem *item = obj->createPickedUpItem();
2303 // Add to inventory and send inventory
2304 ilist->addItem(item);
2305 UpdateCrafting(player->peer_id);
2306 SendInventory(player->peer_id);
2308 // Remove object from environment
2309 obj->m_removed = true;
2314 Item cannot be picked up. Punch it instead.
2317 ToolItem *titem = NULL;
2318 std::string toolname = "";
2320 InventoryList *mlist = player->inventory.getList("main");
2323 InventoryItem *item = mlist->getItem(item_i);
2324 if(item && (std::string)item->getName() == "ToolItem")
2326 titem = (ToolItem*)item;
2327 toolname = titem->getToolName();
2331 u16 wear = obj->punch(toolname);
2335 bool weared_out = titem->addWear(wear);
2337 mlist->deleteItem(item_i);
2338 SendInventory(player->peer_id);
2344 else if(command == TOSERVER_GROUND_ACTION)
2352 [3] v3s16 nodepos_undersurface
2353 [9] v3s16 nodepos_abovesurface
2358 2: stop digging (all parameters ignored)
2359 3: digging completed
2361 u8 action = readU8(&data[2]);
2363 p_under.X = readS16(&data[3]);
2364 p_under.Y = readS16(&data[5]);
2365 p_under.Z = readS16(&data[7]);
2367 p_over.X = readS16(&data[9]);
2368 p_over.Y = readS16(&data[11]);
2369 p_over.Z = readS16(&data[13]);
2370 u16 item_i = readU16(&data[15]);
2372 //TODO: Check that target is reasonably close
2380 NOTE: This can be used in the future to check if
2381 somebody is cheating, by checking the timing.
2388 else if(action == 2)
2391 RemoteClient *client = getClient(peer->id);
2392 JMutexAutoLock digmutex(client->m_dig_mutex);
2393 client->m_dig_tool_item = -1;
2398 3: Digging completed
2400 else if(action == 3)
2402 // Mandatory parameter; actually used for nothing
2403 core::map<v3s16, MapBlock*> modified_blocks;
2405 u8 material = CONTENT_IGNORE;
2406 u8 mineral = MINERAL_NONE;
2408 bool cannot_remove_node = false;
2412 MapNode n = m_env.getMap().getNode(p_under);
2414 mineral = n.getMineral();
2415 // Get material at position
2417 // If not yet cancelled
2418 if(cannot_remove_node == false)
2420 // If it's not diggable, do nothing
2421 if(content_diggable(material) == false)
2423 derr_server<<"Server: Not finishing digging: "
2424 <<"Node not diggable"
2426 cannot_remove_node = true;
2429 // If not yet cancelled
2430 if(cannot_remove_node == false)
2432 // Get node metadata
2433 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2434 if(meta && meta->nodeRemovalDisabled() == true)
2436 derr_server<<"Server: Not finishing digging: "
2437 <<"Node metadata disables removal"
2439 cannot_remove_node = true;
2443 catch(InvalidPositionException &e)
2445 derr_server<<"Server: Not finishing digging: Node not found."
2446 <<" Adding block to emerge queue."
2448 m_emerge_queue.addBlock(peer_id,
2449 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2450 cannot_remove_node = true;
2453 // Make sure the player is allowed to do it
2454 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2456 dstream<<"Player "<<player->getName()<<" cannot remove node"
2457 <<" because privileges are "<<getPlayerPrivs(player)
2459 cannot_remove_node = true;
2463 If node can't be removed, set block to be re-sent to
2466 if(cannot_remove_node)
2468 derr_server<<"Server: Not finishing digging."<<std::endl;
2470 // Client probably has wrong data.
2471 // Set block not sent, so that client will get
2473 dstream<<"Client "<<peer_id<<" tried to dig "
2474 <<"node; but node cannot be removed."
2475 <<" setting MapBlock not sent."<<std::endl;
2476 RemoteClient *client = getClient(peer_id);
2477 v3s16 blockpos = getNodeBlockPos(p_under);
2478 client->SetBlockNotSent(blockpos);
2484 Send the removal to all other clients.
2485 - If other player is close, send REMOVENODE
2486 - Otherwise set blocks not sent
2488 core::list<u16> far_players;
2489 sendRemoveNode(p_under, peer_id, &far_players, 100);
2492 Update and send inventory
2495 if(g_settings.getBool("creative_mode") == false)
2500 InventoryList *mlist = player->inventory.getList("main");
2503 InventoryItem *item = mlist->getItem(item_i);
2504 if(item && (std::string)item->getName() == "ToolItem")
2506 ToolItem *titem = (ToolItem*)item;
2507 std::string toolname = titem->getToolName();
2509 // Get digging properties for material and tool
2510 DiggingProperties prop =
2511 getDiggingProperties(material, toolname);
2513 if(prop.diggable == false)
2515 derr_server<<"Server: WARNING: Player digged"
2516 <<" with impossible material + tool"
2517 <<" combination"<<std::endl;
2520 bool weared_out = titem->addWear(prop.wear);
2524 mlist->deleteItem(item_i);
2530 Add dug item to inventory
2533 InventoryItem *item = NULL;
2535 if(mineral != MINERAL_NONE)
2536 item = getDiggedMineralItem(mineral);
2541 std::string &dug_s = content_features(material).dug_item;
2544 std::istringstream is(dug_s, std::ios::binary);
2545 item = InventoryItem::deSerialize(is);
2551 // Add a item to inventory
2552 player->inventory.addItem("main", item);
2555 UpdateCrafting(player->peer_id);
2556 SendInventory(player->peer_id);
2562 (this takes some time so it is done after the quick stuff)
2564 m_ignore_map_edit_events = true;
2565 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2566 m_ignore_map_edit_events = false;
2569 Set blocks not sent to far players
2571 for(core::list<u16>::Iterator
2572 i = far_players.begin();
2573 i != far_players.end(); i++)
2576 RemoteClient *client = getClient(peer_id);
2579 client->SetBlocksNotSent(modified_blocks);
2586 else if(action == 1)
2589 InventoryList *ilist = player->inventory.getList("main");
2594 InventoryItem *item = ilist->getItem(item_i);
2596 // If there is no item, it is not possible to add it anywhere
2601 Handle material items
2603 if(std::string("MaterialItem") == item->getName())
2606 // Don't add a node if this is not a free space
2607 MapNode n2 = m_env.getMap().getNode(p_over);
2608 bool no_enough_privs =
2609 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2611 dstream<<"Player "<<player->getName()<<" cannot add node"
2612 <<" because privileges are "<<getPlayerPrivs(player)
2615 if(content_buildable_to(n2.d) == false
2618 // Client probably has wrong data.
2619 // Set block not sent, so that client will get
2621 dstream<<"Client "<<peer_id<<" tried to place"
2622 <<" node in invalid position; setting"
2623 <<" MapBlock not sent."<<std::endl;
2624 RemoteClient *client = getClient(peer_id);
2625 v3s16 blockpos = getNodeBlockPos(p_over);
2626 client->SetBlockNotSent(blockpos);
2630 catch(InvalidPositionException &e)
2632 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2633 <<" Adding block to emerge queue."
2635 m_emerge_queue.addBlock(peer_id,
2636 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2640 // Reset build time counter
2641 getClient(peer->id)->m_time_from_building = 0.0;
2644 MaterialItem *mitem = (MaterialItem*)item;
2646 n.d = mitem->getMaterial();
2647 if(content_features(n.d).wall_mounted)
2648 n.dir = packDir(p_under - p_over);
2653 core::list<u16> far_players;
2654 sendAddNode(p_over, n, 0, &far_players, 100);
2659 InventoryList *ilist = player->inventory.getList("main");
2660 if(g_settings.getBool("creative_mode") == false && ilist)
2662 // Remove from inventory and send inventory
2663 if(mitem->getCount() == 1)
2664 ilist->deleteItem(item_i);
2668 UpdateCrafting(peer_id);
2669 SendInventory(peer_id);
2675 This takes some time so it is done after the quick stuff
2677 core::map<v3s16, MapBlock*> modified_blocks;
2678 m_ignore_map_edit_events = true;
2679 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2680 m_ignore_map_edit_events = false;
2683 Set blocks not sent to far players
2685 for(core::list<u16>::Iterator
2686 i = far_players.begin();
2687 i != far_players.end(); i++)
2690 RemoteClient *client = getClient(peer_id);
2693 client->SetBlocksNotSent(modified_blocks);
2697 Calculate special events
2700 /*if(n.d == CONTENT_MESE)
2703 for(s16 z=-1; z<=1; z++)
2704 for(s16 y=-1; y<=1; y++)
2705 for(s16 x=-1; x<=1; x++)
2712 Place other item (not a block)
2716 v3s16 blockpos = getNodeBlockPos(p_over);
2719 Check that the block is loaded so that the item
2720 can properly be added to the static list too
2722 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2725 derr_server<<"Error while placing object: "
2726 "block not found"<<std::endl;
2730 dout_server<<"Placing a miscellaneous item on map"
2733 // Calculate a position for it
2734 v3f pos = intToFloat(p_over, BS);
2736 pos.Y -= BS*0.25; // let it drop a bit
2738 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2739 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2744 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2748 derr_server<<"WARNING: item resulted in NULL object, "
2749 <<"not placing onto map"
2754 // Add the object to the environment
2755 m_env.addActiveObject(obj);
2757 dout_server<<"Placed object"<<std::endl;
2759 if(g_settings.getBool("creative_mode") == false)
2761 // Delete the right amount of items from the slot
2762 u16 dropcount = item->getDropCount();
2764 // Delete item if all gone
2765 if(item->getCount() <= dropcount)
2767 if(item->getCount() < dropcount)
2768 dstream<<"WARNING: Server: dropped more items"
2769 <<" than the slot contains"<<std::endl;
2771 InventoryList *ilist = player->inventory.getList("main");
2773 // Remove from inventory and send inventory
2774 ilist->deleteItem(item_i);
2776 // Else decrement it
2778 item->remove(dropcount);
2781 UpdateCrafting(peer_id);
2782 SendInventory(peer_id);
2790 Catch invalid actions
2794 derr_server<<"WARNING: Server: Invalid action "
2795 <<action<<std::endl;
2799 else if(command == TOSERVER_RELEASE)
2808 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2811 else if(command == TOSERVER_SIGNTEXT)
2813 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2822 std::string datastring((char*)&data[2], datasize-2);
2823 std::istringstream is(datastring, std::ios_base::binary);
2826 is.read((char*)buf, 6);
2827 v3s16 blockpos = readV3S16(buf);
2828 is.read((char*)buf, 2);
2829 s16 id = readS16(buf);
2830 is.read((char*)buf, 2);
2831 u16 textlen = readU16(buf);
2833 for(u16 i=0; i<textlen; i++)
2835 is.read((char*)buf, 1);
2836 text += (char)buf[0];
2839 MapBlock *block = NULL;
2842 block = m_env.getMap().getBlockNoCreate(blockpos);
2844 catch(InvalidPositionException &e)
2846 derr_server<<"Error while setting sign text: "
2847 "block not found"<<std::endl;
2851 MapBlockObject *obj = block->getObject(id);
2854 derr_server<<"Error while setting sign text: "
2855 "object not found"<<std::endl;
2859 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2861 derr_server<<"Error while setting sign text: "
2862 "object is not a sign"<<std::endl;
2866 ((SignObject*)obj)->setText(text);
2868 obj->getBlock()->setChangedFlag();
2870 else if(command == TOSERVER_SIGNNODETEXT)
2872 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2880 std::string datastring((char*)&data[2], datasize-2);
2881 std::istringstream is(datastring, std::ios_base::binary);
2884 is.read((char*)buf, 6);
2885 v3s16 p = readV3S16(buf);
2886 is.read((char*)buf, 2);
2887 u16 textlen = readU16(buf);
2889 for(u16 i=0; i<textlen; i++)
2891 is.read((char*)buf, 1);
2892 text += (char)buf[0];
2895 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2898 if(meta->typeId() != CONTENT_SIGN_WALL)
2900 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2901 signmeta->setText(text);
2903 v3s16 blockpos = getNodeBlockPos(p);
2904 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2907 block->setChangedFlag();
2910 for(core::map<u16, RemoteClient*>::Iterator
2911 i = m_clients.getIterator();
2912 i.atEnd()==false; i++)
2914 RemoteClient *client = i.getNode()->getValue();
2915 client->SetBlockNotSent(blockpos);
2918 else if(command == TOSERVER_INVENTORY_ACTION)
2920 /*// Ignore inventory changes if in creative mode
2921 if(g_settings.getBool("creative_mode") == true)
2923 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2927 // Strip command and create a stream
2928 std::string datastring((char*)&data[2], datasize-2);
2929 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2930 std::istringstream is(datastring, std::ios_base::binary);
2932 InventoryAction *a = InventoryAction::deSerialize(is);
2937 c.current_player = player;
2940 Handle craftresult specially if not in creative mode
2942 bool disable_action = false;
2943 if(a->getType() == IACTION_MOVE
2944 && g_settings.getBool("creative_mode") == false)
2946 IMoveAction *ma = (IMoveAction*)a;
2947 if(ma->to_inv == "current_player" &&
2948 ma->from_inv == "current_player")
2950 InventoryList *rlist = player->inventory.getList("craftresult");
2952 InventoryList *clist = player->inventory.getList("craft");
2954 InventoryList *mlist = player->inventory.getList("main");
2957 Craftresult is no longer preview if something
2960 if(ma->to_list == "craftresult"
2961 && ma->from_list != "craftresult")
2963 // If it currently is a preview, remove
2965 if(player->craftresult_is_preview)
2967 rlist->deleteItem(0);
2969 player->craftresult_is_preview = false;
2972 Crafting takes place if this condition is true.
2974 if(player->craftresult_is_preview &&
2975 ma->from_list == "craftresult")
2977 player->craftresult_is_preview = false;
2978 clist->decrementMaterials(1);
2981 If the craftresult is placed on itself, move it to
2982 main inventory instead of doing the action
2984 if(ma->to_list == "craftresult"
2985 && ma->from_list == "craftresult")
2987 disable_action = true;
2989 InventoryItem *item1 = rlist->changeItem(0, NULL);
2990 mlist->addItem(item1);
2995 if(disable_action == false)
2997 // Feed action to player inventory
3005 UpdateCrafting(player->peer_id);
3006 SendInventory(player->peer_id);
3011 dstream<<"TOSERVER_INVENTORY_ACTION: "
3012 <<"InventoryAction::deSerialize() returned NULL"
3016 else if(command == TOSERVER_CHAT_MESSAGE)
3024 std::string datastring((char*)&data[2], datasize-2);
3025 std::istringstream is(datastring, std::ios_base::binary);
3028 is.read((char*)buf, 2);
3029 u16 len = readU16(buf);
3031 std::wstring message;
3032 for(u16 i=0; i<len; i++)
3034 is.read((char*)buf, 2);
3035 message += (wchar_t)readU16(buf);
3038 // Get player name of this client
3039 std::wstring name = narrow_to_wide(player->getName());
3041 // Line to send to players
3043 // Whether to send to the player that sent the line
3044 bool send_to_sender = false;
3045 // Whether to send to other players
3046 bool send_to_others = false;
3048 // Local player gets all privileges regardless of
3049 // what's set on their account.
3050 u64 privs = getPlayerPrivs(player);
3053 std::wstring commandprefix = L"/#";
3054 if(message.substr(0, commandprefix.size()) == commandprefix)
3056 line += L"Server: ";
3058 message = message.substr(commandprefix.size());
3060 ServerCommandContext *ctx = new ServerCommandContext(
3061 str_split(message, L' '),
3067 line += processServerCommand(ctx);
3068 send_to_sender = ctx->flags & 1;
3069 send_to_others = ctx->flags & 2;
3075 if(privs & PRIV_SHOUT)
3081 send_to_others = true;
3085 line += L"Server: You are not allowed to shout";
3086 send_to_sender = true;
3092 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3095 Send the message to clients
3097 for(core::map<u16, RemoteClient*>::Iterator
3098 i = m_clients.getIterator();
3099 i.atEnd() == false; i++)
3101 // Get client and check that it is valid
3102 RemoteClient *client = i.getNode()->getValue();
3103 assert(client->peer_id == i.getNode()->getKey());
3104 if(client->serialization_version == SER_FMT_VER_INVALID)
3108 bool sender_selected = (peer_id == client->peer_id);
3109 if(sender_selected == true && send_to_sender == false)
3111 if(sender_selected == false && send_to_others == false)
3114 SendChatMessage(client->peer_id, line);
3118 else if(command == TOSERVER_DAMAGE)
3120 if(g_settings.getBool("enable_damage"))
3122 std::string datastring((char*)&data[2], datasize-2);
3123 std::istringstream is(datastring, std::ios_base::binary);
3124 u8 damage = readU8(is);
3125 if(player->hp > damage)
3127 player->hp -= damage;
3133 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3136 v3f pos = findSpawnPos(m_env.getServerMap());
3137 player->setPosition(pos);
3139 SendMovePlayer(player);
3140 SendPlayerHP(player);
3142 //TODO: Throw items around
3146 SendPlayerHP(player);
3148 else if(command == TOSERVER_PASSWORD)
3151 [0] u16 TOSERVER_PASSWORD
3152 [2] u8[28] old password
3153 [30] u8[28] new password
3156 if(datasize != 2+PASSWORD_SIZE*2)
3158 /*char password[PASSWORD_SIZE];
3159 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3160 password[i] = data[2+i];
3161 password[PASSWORD_SIZE-1] = 0;*/
3163 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3171 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3173 char c = data[2+PASSWORD_SIZE+i];
3179 std::string playername = player->getName();
3181 if(m_authmanager.exists(playername) == false)
3183 dstream<<"Server: playername not found in authmanager"<<std::endl;
3184 // Wrong old password supplied!!
3185 SendChatMessage(peer_id, L"playername not found in authmanager");
3189 std::string checkpwd = m_authmanager.getPassword(playername);
3191 if(oldpwd != checkpwd)
3193 dstream<<"Server: invalid old password"<<std::endl;
3194 // Wrong old password supplied!!
3195 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3199 m_authmanager.setPassword(playername, newpwd);
3201 dstream<<"Server: password change successful for "<<playername
3203 SendChatMessage(peer_id, L"Password change successful");
3207 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3208 "unknown command "<<command<<std::endl;
3212 catch(SendFailedException &e)
3214 derr_server<<"Server::ProcessData(): SendFailedException: "
3220 void Server::onMapEditEvent(MapEditEvent *event)
3222 dstream<<"Server::onMapEditEvent()"<<std::endl;
3223 if(m_ignore_map_edit_events)
3225 MapEditEvent *e = event->clone();
3226 m_unsent_map_edit_queue.push_back(e);
3229 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3231 if(id == "current_player")
3233 assert(c->current_player);
3234 return &(c->current_player->inventory);
3238 std::string id0 = fn.next(":");
3240 if(id0 == "nodemeta")
3243 p.X = stoi(fn.next(","));
3244 p.Y = stoi(fn.next(","));
3245 p.Z = stoi(fn.next(","));
3246 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3248 return meta->getInventory();
3249 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3250 <<"no metadata found"<<std::endl;
3254 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3257 void Server::inventoryModified(InventoryContext *c, std::string id)
3259 if(id == "current_player")
3261 assert(c->current_player);
3263 UpdateCrafting(c->current_player->peer_id);
3264 SendInventory(c->current_player->peer_id);
3269 std::string id0 = fn.next(":");
3271 if(id0 == "nodemeta")
3274 p.X = stoi(fn.next(","));
3275 p.Y = stoi(fn.next(","));
3276 p.Z = stoi(fn.next(","));
3277 v3s16 blockpos = getNodeBlockPos(p);
3279 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3281 meta->inventoryModified();
3283 for(core::map<u16, RemoteClient*>::Iterator
3284 i = m_clients.getIterator();
3285 i.atEnd()==false; i++)
3287 RemoteClient *client = i.getNode()->getValue();
3288 client->SetBlockNotSent(blockpos);
3294 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3297 core::list<PlayerInfo> Server::getPlayerInfo()
3299 DSTACK(__FUNCTION_NAME);
3300 JMutexAutoLock envlock(m_env_mutex);
3301 JMutexAutoLock conlock(m_con_mutex);
3303 core::list<PlayerInfo> list;
3305 core::list<Player*> players = m_env.getPlayers();
3307 core::list<Player*>::Iterator i;
3308 for(i = players.begin();
3309 i != players.end(); i++)
3313 Player *player = *i;
3316 con::Peer *peer = m_con.GetPeer(player->peer_id);
3317 // Copy info from peer to info struct
3319 info.address = peer->address;
3320 info.avg_rtt = peer->avg_rtt;
3322 catch(con::PeerNotFoundException &e)
3324 // Set dummy peer info
3326 info.address = Address(0,0,0,0,0);
3330 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3331 info.position = player->getPosition();
3333 list.push_back(info);
3340 void Server::peerAdded(con::Peer *peer)
3342 DSTACK(__FUNCTION_NAME);
3343 dout_server<<"Server::peerAdded(): peer->id="
3344 <<peer->id<<std::endl;
3347 c.type = PEER_ADDED;
3348 c.peer_id = peer->id;
3350 m_peer_change_queue.push_back(c);
3353 void Server::deletingPeer(con::Peer *peer, bool timeout)
3355 DSTACK(__FUNCTION_NAME);
3356 dout_server<<"Server::deletingPeer(): peer->id="
3357 <<peer->id<<", timeout="<<timeout<<std::endl;
3360 c.type = PEER_REMOVED;
3361 c.peer_id = peer->id;
3362 c.timeout = timeout;
3363 m_peer_change_queue.push_back(c);
3370 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3372 DSTACK(__FUNCTION_NAME);
3373 std::ostringstream os(std::ios_base::binary);
3375 writeU16(os, TOCLIENT_HP);
3379 std::string s = os.str();
3380 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3382 con.Send(peer_id, 0, data, true);
3385 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3386 const std::wstring &reason)
3388 DSTACK(__FUNCTION_NAME);
3389 std::ostringstream os(std::ios_base::binary);
3391 writeU16(os, TOCLIENT_ACCESS_DENIED);
3392 os<<serializeWideString(reason);
3395 std::string s = os.str();
3396 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3398 con.Send(peer_id, 0, data, true);
3402 Non-static send methods
3405 void Server::SendObjectData(float dtime)
3407 DSTACK(__FUNCTION_NAME);
3409 core::map<v3s16, bool> stepped_blocks;
3411 for(core::map<u16, RemoteClient*>::Iterator
3412 i = m_clients.getIterator();
3413 i.atEnd() == false; i++)
3415 u16 peer_id = i.getNode()->getKey();
3416 RemoteClient *client = i.getNode()->getValue();
3417 assert(client->peer_id == peer_id);
3419 if(client->serialization_version == SER_FMT_VER_INVALID)
3422 client->SendObjectData(this, dtime, stepped_blocks);
3426 void Server::SendPlayerInfos()
3428 DSTACK(__FUNCTION_NAME);
3430 //JMutexAutoLock envlock(m_env_mutex);
3432 // Get connected players
3433 core::list<Player*> players = m_env.getPlayers(true);
3435 u32 player_count = players.getSize();
3436 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3438 SharedBuffer<u8> data(datasize);
3439 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3442 core::list<Player*>::Iterator i;
3443 for(i = players.begin();
3444 i != players.end(); i++)
3446 Player *player = *i;
3448 /*dstream<<"Server sending player info for player with "
3449 "peer_id="<<player->peer_id<<std::endl;*/
3451 writeU16(&data[start], player->peer_id);
3452 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3453 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3454 start += 2+PLAYERNAME_SIZE;
3457 //JMutexAutoLock conlock(m_con_mutex);
3460 m_con.SendToAll(0, data, true);
3463 void Server::SendInventory(u16 peer_id)
3465 DSTACK(__FUNCTION_NAME);
3467 Player* player = m_env.getPlayer(peer_id);
3474 std::ostringstream os;
3475 //os.imbue(std::locale("C"));
3477 player->inventory.serialize(os);
3479 std::string s = os.str();
3481 SharedBuffer<u8> data(s.size()+2);
3482 writeU16(&data[0], TOCLIENT_INVENTORY);
3483 memcpy(&data[2], s.c_str(), s.size());
3486 m_con.Send(peer_id, 0, data, true);
3489 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3491 DSTACK(__FUNCTION_NAME);
3493 std::ostringstream os(std::ios_base::binary);
3497 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3498 os.write((char*)buf, 2);
3501 writeU16(buf, message.size());
3502 os.write((char*)buf, 2);
3505 for(u32 i=0; i<message.size(); i++)
3509 os.write((char*)buf, 2);
3513 std::string s = os.str();
3514 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3516 m_con.Send(peer_id, 0, data, true);
3519 void Server::BroadcastChatMessage(const std::wstring &message)
3521 for(core::map<u16, RemoteClient*>::Iterator
3522 i = m_clients.getIterator();
3523 i.atEnd() == false; i++)
3525 // Get client and check that it is valid
3526 RemoteClient *client = i.getNode()->getValue();
3527 assert(client->peer_id == i.getNode()->getKey());
3528 if(client->serialization_version == SER_FMT_VER_INVALID)
3531 SendChatMessage(client->peer_id, message);
3535 void Server::SendPlayerHP(Player *player)
3537 SendHP(m_con, player->peer_id, player->hp);
3540 void Server::SendMovePlayer(Player *player)
3542 DSTACK(__FUNCTION_NAME);
3543 std::ostringstream os(std::ios_base::binary);
3545 writeU16(os, TOCLIENT_MOVE_PLAYER);
3546 writeV3F1000(os, player->getPosition());
3547 writeF1000(os, player->getPitch());
3548 writeF1000(os, player->getYaw());
3551 v3f pos = player->getPosition();
3552 f32 pitch = player->getPitch();
3553 f32 yaw = player->getYaw();
3554 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3555 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3562 std::string s = os.str();
3563 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3565 m_con.Send(player->peer_id, 0, data, true);
3568 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3569 core::list<u16> *far_players, float far_d_nodes)
3571 float maxd = far_d_nodes*BS;
3572 v3f p_f = intToFloat(p, BS);
3576 SharedBuffer<u8> reply(replysize);
3577 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3578 writeS16(&reply[2], p.X);
3579 writeS16(&reply[4], p.Y);
3580 writeS16(&reply[6], p.Z);
3582 for(core::map<u16, RemoteClient*>::Iterator
3583 i = m_clients.getIterator();
3584 i.atEnd() == false; i++)
3586 // Get client and check that it is valid
3587 RemoteClient *client = i.getNode()->getValue();
3588 assert(client->peer_id == i.getNode()->getKey());
3589 if(client->serialization_version == SER_FMT_VER_INVALID)
3592 // Don't send if it's the same one
3593 if(client->peer_id == ignore_id)
3599 Player *player = m_env.getPlayer(client->peer_id);
3602 // If player is far away, only set modified blocks not sent
3603 v3f player_pos = player->getPosition();
3604 if(player_pos.getDistanceFrom(p_f) > maxd)
3606 far_players->push_back(client->peer_id);
3613 m_con.Send(client->peer_id, 0, reply, true);
3617 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3618 core::list<u16> *far_players, float far_d_nodes)
3620 float maxd = far_d_nodes*BS;
3621 v3f p_f = intToFloat(p, BS);
3623 for(core::map<u16, RemoteClient*>::Iterator
3624 i = m_clients.getIterator();
3625 i.atEnd() == false; i++)
3627 // Get client and check that it is valid
3628 RemoteClient *client = i.getNode()->getValue();
3629 assert(client->peer_id == i.getNode()->getKey());
3630 if(client->serialization_version == SER_FMT_VER_INVALID)
3633 // Don't send if it's the same one
3634 if(client->peer_id == ignore_id)
3640 Player *player = m_env.getPlayer(client->peer_id);
3643 // If player is far away, only set modified blocks not sent
3644 v3f player_pos = player->getPosition();
3645 if(player_pos.getDistanceFrom(p_f) > maxd)
3647 far_players->push_back(client->peer_id);
3654 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3655 SharedBuffer<u8> reply(replysize);
3656 writeU16(&reply[0], TOCLIENT_ADDNODE);
3657 writeS16(&reply[2], p.X);
3658 writeS16(&reply[4], p.Y);
3659 writeS16(&reply[6], p.Z);
3660 n.serialize(&reply[8], client->serialization_version);
3663 m_con.Send(client->peer_id, 0, reply, true);
3667 void Server::setBlockNotSent(v3s16 p)
3669 for(core::map<u16, RemoteClient*>::Iterator
3670 i = m_clients.getIterator();
3671 i.atEnd()==false; i++)
3673 RemoteClient *client = i.getNode()->getValue();
3674 client->SetBlockNotSent(p);
3678 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3680 DSTACK(__FUNCTION_NAME);
3682 v3s16 p = block->getPos();
3686 bool completely_air = true;
3687 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3688 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3689 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3691 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3693 completely_air = false;
3694 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3699 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3701 dstream<<"[completely air] ";
3706 Create a packet with the block in the right format
3709 std::ostringstream os(std::ios_base::binary);
3710 block->serialize(os, ver);
3711 std::string s = os.str();
3712 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3714 u32 replysize = 8 + blockdata.getSize();
3715 SharedBuffer<u8> reply(replysize);
3716 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3717 writeS16(&reply[2], p.X);
3718 writeS16(&reply[4], p.Y);
3719 writeS16(&reply[6], p.Z);
3720 memcpy(&reply[8], *blockdata, blockdata.getSize());
3722 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3723 <<": \tpacket size: "<<replysize<<std::endl;*/
3728 m_con.Send(peer_id, 1, reply, true);
3731 void Server::SendBlocks(float dtime)
3733 DSTACK(__FUNCTION_NAME);
3735 JMutexAutoLock envlock(m_env_mutex);
3736 JMutexAutoLock conlock(m_con_mutex);
3738 //TimeTaker timer("Server::SendBlocks");
3740 core::array<PrioritySortedBlockTransfer> queue;
3742 s32 total_sending = 0;
3745 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3747 for(core::map<u16, RemoteClient*>::Iterator
3748 i = m_clients.getIterator();
3749 i.atEnd() == false; i++)
3751 RemoteClient *client = i.getNode()->getValue();
3752 assert(client->peer_id == i.getNode()->getKey());
3754 total_sending += client->SendingCount();
3756 if(client->serialization_version == SER_FMT_VER_INVALID)
3759 client->GetNextBlocks(this, dtime, queue);
3764 // Lowest priority number comes first.
3765 // Lowest is most important.
3768 for(u32 i=0; i<queue.size(); i++)
3770 //TODO: Calculate limit dynamically
3771 if(total_sending >= g_settings.getS32
3772 ("max_simultaneous_block_sends_server_total"))
3775 PrioritySortedBlockTransfer q = queue[i];
3777 MapBlock *block = NULL;
3780 block = m_env.getMap().getBlockNoCreate(q.pos);
3782 catch(InvalidPositionException &e)
3787 RemoteClient *client = getClient(q.peer_id);
3789 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3791 client->SentBlock(q.pos);
3801 void Server::UpdateCrafting(u16 peer_id)
3803 DSTACK(__FUNCTION_NAME);
3805 Player* player = m_env.getPlayer(peer_id);
3809 Calculate crafting stuff
3811 if(g_settings.getBool("creative_mode") == false)
3813 InventoryList *clist = player->inventory.getList("craft");
3814 InventoryList *rlist = player->inventory.getList("craftresult");
3816 if(rlist->getUsedSlots() == 0)
3817 player->craftresult_is_preview = true;
3819 if(rlist && player->craftresult_is_preview)
3821 rlist->clearItems();
3823 if(clist && rlist && player->craftresult_is_preview)
3825 InventoryItem *items[9];
3826 for(u16 i=0; i<9; i++)
3828 items[i] = clist->getItem(i);
3837 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3838 if(checkItemCombination(items, specs))
3840 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3849 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3850 if(checkItemCombination(items, specs))
3852 rlist->addItem(new CraftItem("Stick", 4));
3861 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3862 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3863 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3864 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3865 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3866 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3867 if(checkItemCombination(items, specs))
3869 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3878 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3879 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3880 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3883 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3884 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3885 if(checkItemCombination(items, specs))
3887 //rlist->addItem(new MapBlockObjectItem("Sign"));
3888 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3897 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3898 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3899 if(checkItemCombination(items, specs))
3901 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3910 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3911 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3912 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3913 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3914 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3915 if(checkItemCombination(items, specs))
3917 rlist->addItem(new ToolItem("WPick", 0));
3926 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3927 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3928 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3929 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3930 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3931 if(checkItemCombination(items, specs))
3933 rlist->addItem(new ToolItem("STPick", 0));
3942 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3943 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3944 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3945 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3946 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3947 if(checkItemCombination(items, specs))
3949 rlist->addItem(new ToolItem("SteelPick", 0));
3958 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3959 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3960 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3961 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3962 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3963 if(checkItemCombination(items, specs))
3965 rlist->addItem(new ToolItem("MesePick", 0));
3974 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3975 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3976 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3977 if(checkItemCombination(items, specs))
3979 rlist->addItem(new ToolItem("WShovel", 0));
3988 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3989 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3990 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3991 if(checkItemCombination(items, specs))
3993 rlist->addItem(new ToolItem("STShovel", 0));
4002 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4003 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4004 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4005 if(checkItemCombination(items, specs))
4007 rlist->addItem(new ToolItem("SteelShovel", 0));
4016 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4017 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4018 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4019 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4020 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4021 if(checkItemCombination(items, specs))
4023 rlist->addItem(new ToolItem("WAxe", 0));
4032 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4033 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4034 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4035 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4036 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4037 if(checkItemCombination(items, specs))
4039 rlist->addItem(new ToolItem("STAxe", 0));
4048 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4049 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4050 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4051 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4052 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4053 if(checkItemCombination(items, specs))
4055 rlist->addItem(new ToolItem("SteelAxe", 0));
4064 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4065 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4066 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4067 if(checkItemCombination(items, specs))
4069 rlist->addItem(new ToolItem("WSword", 0));
4078 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4079 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4080 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4081 if(checkItemCombination(items, specs))
4083 rlist->addItem(new ToolItem("STSword", 0));
4092 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4093 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4094 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4095 if(checkItemCombination(items, specs))
4097 rlist->addItem(new ToolItem("SteelSword", 0));
4106 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4107 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4108 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4109 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4110 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4111 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4112 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4113 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4114 if(checkItemCombination(items, specs))
4116 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4125 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4126 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4127 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4128 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4129 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4130 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4131 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4132 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4133 if(checkItemCombination(items, specs))
4135 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4144 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4145 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4146 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4147 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4148 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4149 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4150 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4151 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4152 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4153 if(checkItemCombination(items, specs))
4155 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4161 } // if creative_mode == false
4164 RemoteClient* Server::getClient(u16 peer_id)
4166 DSTACK(__FUNCTION_NAME);
4167 //JMutexAutoLock lock(m_con_mutex);
4168 core::map<u16, RemoteClient*>::Node *n;
4169 n = m_clients.find(peer_id);
4170 // A client should exist for all peers
4172 return n->getValue();
4175 std::wstring Server::getStatusString()
4177 std::wostringstream os(std::ios_base::binary);
4180 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4182 os<<L", uptime="<<m_uptime.get();
4183 // Information about clients
4185 for(core::map<u16, RemoteClient*>::Iterator
4186 i = m_clients.getIterator();
4187 i.atEnd() == false; i++)
4189 // Get client and check that it is valid
4190 RemoteClient *client = i.getNode()->getValue();
4191 assert(client->peer_id == i.getNode()->getKey());
4192 if(client->serialization_version == SER_FMT_VER_INVALID)
4195 Player *player = m_env.getPlayer(client->peer_id);
4196 // Get name of player
4197 std::wstring name = L"unknown";
4199 name = narrow_to_wide(player->getName());
4200 // Add name to information string
4204 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4205 os<<" WARNING: Map saving is disabled."<<std::endl;
4210 void setCreativeInventory(Player *player)
4212 player->resetInventory();
4214 // Give some good tools
4216 InventoryItem *item = new ToolItem("MesePick", 0);
4217 void* r = player->inventory.addItem("main", item);
4221 InventoryItem *item = new ToolItem("SteelPick", 0);
4222 void* r = player->inventory.addItem("main", item);
4226 InventoryItem *item = new ToolItem("SteelAxe", 0);
4227 void* r = player->inventory.addItem("main", item);
4231 InventoryItem *item = new ToolItem("SteelShovel", 0);
4232 void* r = player->inventory.addItem("main", item);
4240 // CONTENT_IGNORE-terminated list
4241 u8 material_items[] = {
4252 CONTENT_WATERSOURCE,
4260 u8 *mip = material_items;
4261 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4263 if(*mip == CONTENT_IGNORE)
4266 InventoryItem *item = new MaterialItem(*mip, 1);
4267 player->inventory.addItem("main", item);
4273 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4276 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4277 player->inventory.addItem("main", item);
4280 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4282 // Skip some materials
4283 if(i == CONTENT_WATER || i == CONTENT_TORCH
4284 || i == CONTENT_COALSTONE)
4287 InventoryItem *item = new MaterialItem(i, 1);
4288 player->inventory.addItem("main", item);
4294 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4295 void* r = player->inventory.addItem("main", item);
4300 v3f findSpawnPos(ServerMap &map)
4302 //return v3f(50,50,50)*BS;
4305 s16 groundheight = 0;
4307 // Try to find a good place a few times
4308 for(s32 i=0; i<1000; i++)
4311 // We're going to try to throw the player to this position
4312 nodepos = v2s16(-range + (myrand()%(range*2)),
4313 -range + (myrand()%(range*2)));
4314 v2s16 sectorpos = getNodeSectorPos(nodepos);
4315 // Get sector (NOTE: Don't get because it's slow)
4316 //m_env.getMap().emergeSector(sectorpos);
4317 // Get ground height at point (fallbacks to heightmap function)
4318 groundheight = map.findGroundLevel(nodepos);
4319 // Don't go underwater
4320 if(groundheight < WATER_LEVEL)
4322 //dstream<<"-> Underwater"<<std::endl;
4325 // Don't go to high places
4326 if(groundheight > WATER_LEVEL + 4)
4328 //dstream<<"-> Underwater"<<std::endl;
4332 // Found a good place
4333 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4337 // If no suitable place was not found, go above water at least.
4338 if(groundheight < WATER_LEVEL)
4339 groundheight = WATER_LEVEL;
4341 return intToFloat(v3s16(
4348 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4351 Try to get an existing player
4353 Player *player = m_env.getPlayer(name);
4356 // If player is already connected, cancel
4357 if(player->peer_id != 0)
4359 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4364 player->peer_id = peer_id;
4366 // Reset inventory to creative if in creative mode
4367 if(g_settings.getBool("creative_mode"))
4369 setCreativeInventory(player);
4376 If player with the wanted peer_id already exists, cancel.
4378 if(m_env.getPlayer(peer_id) != NULL)
4380 dstream<<"emergePlayer(): Player with wrong name but same"
4381 " peer_id already exists"<<std::endl;
4389 player = new ServerRemotePlayer();
4390 //player->peer_id = c.peer_id;
4391 //player->peer_id = PEER_ID_INEXISTENT;
4392 player->peer_id = peer_id;
4393 player->updateName(name);
4394 m_authmanager.add(name);
4395 m_authmanager.setPassword(name, password);
4396 m_authmanager.setPrivs(name,
4397 stringToPrivs(g_settings.get("default_privs")));
4403 dstream<<"Server: Finding spawn place for player \""
4404 <<player->getName()<<"\""<<std::endl;
4406 v3f pos = findSpawnPos(m_env.getServerMap());
4408 player->setPosition(pos);
4411 Add player to environment
4414 m_env.addPlayer(player);
4417 Add stuff to inventory
4420 if(g_settings.getBool("creative_mode"))
4422 setCreativeInventory(player);
4424 else if(g_settings.getBool("give_initial_stuff"))
4427 InventoryItem *item = new ToolItem("SteelPick", 0);
4428 void* r = player->inventory.addItem("main", item);
4432 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4433 void* r = player->inventory.addItem("main", item);
4437 InventoryItem *item = new ToolItem("SteelAxe", 0);
4438 void* r = player->inventory.addItem("main", item);
4442 InventoryItem *item = new ToolItem("SteelShovel", 0);
4443 void* r = player->inventory.addItem("main", item);
4447 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4448 void* r = player->inventory.addItem("main", item);
4452 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4453 void* r = player->inventory.addItem("main", item);
4457 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4458 void* r = player->inventory.addItem("main", item);
4462 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4463 void* r = player->inventory.addItem("main", item);
4467 InventoryItem *item = new CraftItem("Stick", 4);
4468 void* r = player->inventory.addItem("main", item);
4472 InventoryItem *item = new ToolItem("WPick", 32000);
4473 void* r = player->inventory.addItem("main", item);
4477 InventoryItem *item = new ToolItem("STPick", 32000);
4478 void* r = player->inventory.addItem("main", item);
4482 for(u16 i=0; i<4; i++)
4484 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4485 bool r = player->inventory.addItem("main", item);
4488 /*// Give some other stuff
4490 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4491 bool r = player->inventory.addItem("main", item);
4498 } // create new player
4501 void Server::handlePeerChange(PeerChange &c)
4503 JMutexAutoLock envlock(m_env_mutex);
4504 JMutexAutoLock conlock(m_con_mutex);
4506 if(c.type == PEER_ADDED)
4513 core::map<u16, RemoteClient*>::Node *n;
4514 n = m_clients.find(c.peer_id);
4515 // The client shouldn't already exist
4519 RemoteClient *client = new RemoteClient();
4520 client->peer_id = c.peer_id;
4521 m_clients.insert(client->peer_id, client);
4524 else if(c.type == PEER_REMOVED)
4531 core::map<u16, RemoteClient*>::Node *n;
4532 n = m_clients.find(c.peer_id);
4533 // The client should exist
4537 Mark objects to be not known by the client
4539 RemoteClient *client = n->getValue();
4541 for(core::map<u16, bool>::Iterator
4542 i = client->m_known_objects.getIterator();
4543 i.atEnd()==false; i++)
4546 u16 id = i.getNode()->getKey();
4547 ServerActiveObject* obj = m_env.getActiveObject(id);
4549 if(obj && obj->m_known_by_count > 0)
4550 obj->m_known_by_count--;
4553 // Collect information about leaving in chat
4554 std::wstring message;
4556 std::wstring name = L"unknown";
4557 Player *player = m_env.getPlayer(c.peer_id);
4559 name = narrow_to_wide(player->getName());
4563 message += L" left game";
4565 message += L" (timed out)";
4570 m_env.removePlayer(c.peer_id);
4573 // Set player client disconnected
4575 Player *player = m_env.getPlayer(c.peer_id);
4577 player->peer_id = 0;
4581 delete m_clients[c.peer_id];
4582 m_clients.remove(c.peer_id);
4584 // Send player info to all remaining clients
4587 // Send leave chat message to all remaining clients
4588 BroadcastChatMessage(message);
4597 void Server::handlePeerChanges()
4599 while(m_peer_change_queue.size() > 0)
4601 PeerChange c = m_peer_change_queue.pop_front();
4603 dout_server<<"Server: Handling peer change: "
4604 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4607 handlePeerChange(c);
4611 u64 Server::getPlayerPrivs(Player *player)
4615 std::string playername = player->getName();
4616 // Local player gets all privileges regardless of
4617 // what's set on their account.
4618 if(g_settings.get("name") == playername)
4624 return getPlayerAuthPrivs(playername);
4628 void dedicated_server_loop(Server &server, bool &kill)
4630 DSTACK(__FUNCTION_NAME);
4632 dstream<<DTIME<<std::endl;
4633 dstream<<"========================"<<std::endl;
4634 dstream<<"Running dedicated server"<<std::endl;
4635 dstream<<"========================"<<std::endl;
4638 IntervalLimiter m_profiler_interval;
4642 // This is kind of a hack but can be done like this
4643 // because server.step() is very light
4645 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4650 if(server.getShutdownRequested() || kill)
4652 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4659 float profiler_print_interval =
4660 g_settings.getFloat("profiler_print_interval");
4661 if(profiler_print_interval != 0)
4663 if(m_profiler_interval.step(0.030, profiler_print_interval))
4665 dstream<<"Profiler:"<<std::endl;
4666 g_profiler.print(dstream);
4674 static int counter = 0;
4680 core::list<PlayerInfo> list = server.getPlayerInfo();
4681 core::list<PlayerInfo>::Iterator i;
4682 static u32 sum_old = 0;
4683 u32 sum = PIChecksum(list);
4686 dstream<<DTIME<<"Player info:"<<std::endl;
4687 for(i=list.begin(); i!=list.end(); i++)
4689 i->PrintLine(&dstream);