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"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 //TimeTaker timer("AsyncRunStep() + Receive()");
51 //TimeTaker timer("AsyncRunStep()");
52 m_server->AsyncRunStep();
55 //dout_server<<"Running m_server->Receive()"<<std::endl;
58 catch(con::NoIncomingDataException &e)
61 catch(con::PeerNotFoundException &e)
63 dout_server<<"Server: PeerNotFoundException"<<std::endl;
67 END_DEBUG_EXCEPTION_HANDLER
72 void * EmergeThread::Thread()
76 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
83 Get block info from queue, emerge them and send them
86 After queue is empty, exit.
90 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
94 SharedPtr<QueuedBlockEmerge> q(qptr);
100 Do not generate over-limit
102 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
110 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
112 //TimeTaker timer("block emerge");
115 Try to emerge it from somewhere.
117 If it is only wanted as optional, only loading from disk
122 Check if any peer wants it as non-optional. In that case it
125 Also decrement the emerge queue count in clients.
128 bool optional = true;
131 core::map<u16, u8>::Iterator i;
132 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
134 //u16 peer_id = i.getNode()->getKey();
137 u8 flags = i.getNode()->getValue();
138 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
144 /*dstream<<"EmergeThread: p="
145 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
146 <<"optional="<<optional<<std::endl;*/
148 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
150 core::map<v3s16, MapBlock*> changed_blocks;
151 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
153 MapBlock *block = NULL;
154 bool got_block = true;
155 core::map<v3s16, MapBlock*> modified_blocks;
157 bool only_from_disk = false;
160 only_from_disk = true;
162 v2s16 chunkpos = map.sector_to_chunk(p2d);
164 bool generate_chunk = false;
165 if(only_from_disk == false)
167 JMutexAutoLock envlock(m_server->m_env_mutex);
168 if(map.chunkNonVolatile(chunkpos) == false)
169 generate_chunk = true;
176 JMutexAutoLock envlock(m_server->m_env_mutex);
177 map.initChunkMake(data, chunkpos);
183 JMutexAutoLock envlock(m_server->m_env_mutex);
184 map.finishChunkMake(data, changed_blocks);
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 map.loadSectorFull(p2d);
198 block = map.getBlockNoCreateNoEx(p);
199 if(!block || block->isDummy())
207 // Get, load or create sector
208 ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);
211 block = map.generateBlock(p, block, sector, changed_blocks,
212 lighting_invalidated_blocks);
219 if(block->getLightingExpired()){
220 lighting_invalidated_blocks[block->getPos()] = block;
224 // TODO: Some additional checking and lighting updating,
229 JMutexAutoLock envlock(m_server->m_env_mutex);
234 Collect a list of blocks that have been modified in
235 addition to the fetched one.
238 if(lighting_invalidated_blocks.size() > 0)
240 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
241 <<" blocks"<<std::endl;*/
243 // 50-100ms for single block generation
244 //TimeTaker timer("** EmergeThread updateLighting");
246 // Update lighting without locking the environment mutex,
247 // add modified blocks to changed blocks
248 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
251 // Add all from changed_blocks to modified_blocks
252 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
253 i.atEnd() == false; i++)
255 MapBlock *block = i.getNode()->getValue();
256 modified_blocks.insert(block->getPos(), block);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
312 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
315 m_nearest_unsent_reset_timer += dtime;
316 m_nothing_to_send_pause_timer -= dtime;
318 if(m_nothing_to_send_pause_timer >= 0)
321 // Won't send anything if already sending
322 if(m_blocks_sending.size() >= g_settings.getU16
323 ("max_simultaneous_block_sends_per_client"))
325 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
329 Player *player = server->m_env.getPlayer(peer_id);
331 assert(player != NULL);
333 v3f playerpos = player->getPosition();
334 v3f playerspeed = player->getSpeed();
336 v3s16 center_nodepos = floatToInt(playerpos, BS);
338 v3s16 center = getNodeBlockPos(center_nodepos);
340 // Camera position and direction
342 playerpos + v3f(0, BS+BS/2, 0);
343 v3f camera_dir = v3f(0,0,1);
344 camera_dir.rotateYZBy(player->getPitch());
345 camera_dir.rotateXZBy(player->getYaw());
348 Get the starting value of the block finder radius.
351 if(m_last_center != center)
353 m_nearest_unsent_d = 0;
354 m_last_center = center;
357 /*dstream<<"m_nearest_unsent_reset_timer="
358 <<m_nearest_unsent_reset_timer<<std::endl;*/
359 if(m_nearest_unsent_reset_timer > 5.0)
361 m_nearest_unsent_reset_timer = 0;
362 m_nearest_unsent_d = 0;
363 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
366 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
367 s16 d_start = m_nearest_unsent_d;
369 //dstream<<"d_start="<<d_start<<std::endl;
371 u16 max_simul_sends_setting = g_settings.getU16
372 ("max_simultaneous_block_sends_per_client");
373 u16 max_simul_sends_usually = max_simul_sends_setting;
376 Check the time from last addNode/removeNode.
378 Decrease send rate if player is building stuff.
380 m_time_from_building += dtime;
381 if(m_time_from_building < g_settings.getFloat(
382 "full_block_send_enable_min_time_from_building"))
384 max_simul_sends_usually
385 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
389 Number of blocks sending + number of blocks selected for sending
391 u32 num_blocks_selected = m_blocks_sending.size();
394 next time d will be continued from the d from which the nearest
395 unsent block was found this time.
397 This is because not necessarily any of the blocks found this
398 time are actually sent.
400 s32 new_nearest_unsent_d = -1;
402 s16 d_max = g_settings.getS16("max_block_send_distance");
403 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
405 //dstream<<"Starting from "<<d_start<<std::endl;
407 bool sending_something = false;
409 for(s16 d = d_start; d <= d_max; d++)
411 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
414 If m_nearest_unsent_d was changed by the EmergeThread
415 (it can change it to 0 through SetBlockNotSent),
417 Else update m_nearest_unsent_d
419 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
421 d = m_nearest_unsent_d;
422 last_nearest_unsent_d = m_nearest_unsent_d;
426 Get the border/face dot coordinates of a "d-radiused"
429 core::list<v3s16> list;
430 getFacePositions(list, d);
432 core::list<v3s16>::Iterator li;
433 for(li=list.begin(); li!=list.end(); li++)
435 v3s16 p = *li + center;
439 - Don't allow too many simultaneous transfers
440 - EXCEPT when the blocks are very close
442 Also, don't send blocks that are already flying.
445 // Start with the usual maximum
446 u16 max_simul_dynamic = max_simul_sends_usually;
448 // If block is very close, allow full maximum
449 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
450 max_simul_dynamic = max_simul_sends_setting;
452 // Don't select too many blocks for sending
453 if(num_blocks_selected >= max_simul_dynamic)
456 // Don't send blocks that are currently being transferred
457 if(m_blocks_sending.find(p) != NULL)
463 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
464 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
465 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
466 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
467 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
468 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
471 // If this is true, inexistent block will be made from scratch
472 bool generate = d <= d_max_gen;
475 /*// Limit the generating area vertically to 2/3
476 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
479 // Limit the send area vertically to 2/3
480 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
486 If block is far away, don't generate it unless it is
492 // Block center y in nodes
493 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
494 // Don't generate if it's very high or very low
495 if(y < -64 || y > 64)
499 v2s16 p2d_nodes_center(
503 // Get ground height in nodes
504 s16 gh = server->m_env.getServerMap().findGroundLevel(
507 // If differs a lot, don't generate
508 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
510 // Actually, don't even send it
517 Don't generate or send if not in sight
520 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
526 Don't send already sent blocks
529 if(m_blocks_sent.find(p) != NULL)
534 Check if map has this block
536 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
538 bool surely_not_found_on_disk = false;
539 bool block_is_invalid = false;
544 surely_not_found_on_disk = true;
547 if(block->isValid() == false)
549 block_is_invalid = true;
552 /*if(block->isFullyGenerated() == false)
554 block_is_invalid = true;
558 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
559 v2s16 chunkpos = map->sector_to_chunk(p2d);
560 if(map->chunkNonVolatile(chunkpos) == false)
561 block_is_invalid = true;
564 If block is not close, don't send it unless it is near
567 Block is not near ground level if night-time mesh
568 doesn't differ from day-time mesh.
572 if(block->dayNightDiffed() == false)
579 If block has been marked to not exist on disk (dummy)
580 and generating new ones is not wanted, skip block.
582 if(generate == false && surely_not_found_on_disk == true)
589 Record the lowest d from which a a block has been
590 found being not sent and possibly to exist
592 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
595 new_nearest_unsent_d = d;
599 Add inexistent block to emerge queue.
601 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
603 //TODO: Get value from somewhere
604 // Allow only one block in emerge queue
605 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
606 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
608 //dstream<<"Adding block to emerge queue"<<std::endl;
610 // Add it to the emerge queue and trigger the thread
613 if(generate == false)
614 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
616 server->m_emerge_queue.addBlock(peer_id, p, flags);
617 server->m_emergethread.trigger();
625 Add block to send queue
628 PrioritySortedBlockTransfer q((float)d, p, peer_id);
632 num_blocks_selected += 1;
633 sending_something = true;
638 if(new_nearest_unsent_d != -1)
640 m_nearest_unsent_d = new_nearest_unsent_d;
643 if(sending_something == false)
645 m_nothing_to_send_counter++;
646 if(m_nothing_to_send_counter >= 3)
648 // Pause time in seconds
649 m_nothing_to_send_pause_timer = 2.0;
654 m_nothing_to_send_counter = 0;
657 /*timer_result = timer.stop(true);
658 if(timer_result != 0)
659 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
662 void RemoteClient::SendObjectData(
665 core::map<v3s16, bool> &stepped_blocks
668 DSTACK(__FUNCTION_NAME);
670 // Can't send anything without knowing version
671 if(serialization_version == SER_FMT_VER_INVALID)
673 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
679 Send a TOCLIENT_OBJECTDATA packet.
683 u16 number of player positions
694 std::ostringstream os(std::ios_base::binary);
698 writeU16(buf, TOCLIENT_OBJECTDATA);
699 os.write((char*)buf, 2);
702 Get and write player data
705 // Get connected players
706 core::list<Player*> players = server->m_env.getPlayers(true);
708 // Write player count
709 u16 playercount = players.size();
710 writeU16(buf, playercount);
711 os.write((char*)buf, 2);
713 core::list<Player*>::Iterator i;
714 for(i = players.begin();
715 i != players.end(); i++)
719 v3f pf = player->getPosition();
720 v3f sf = player->getSpeed();
722 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
723 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
724 s32 pitch_i (player->getPitch() * 100);
725 s32 yaw_i (player->getYaw() * 100);
727 writeU16(buf, player->peer_id);
728 os.write((char*)buf, 2);
729 writeV3S32(buf, position_i);
730 os.write((char*)buf, 12);
731 writeV3S32(buf, speed_i);
732 os.write((char*)buf, 12);
733 writeS32(buf, pitch_i);
734 os.write((char*)buf, 4);
735 writeS32(buf, yaw_i);
736 os.write((char*)buf, 4);
740 Get and write object data
746 For making players to be able to build to their nearby
747 environment (building is not possible on blocks that are not
750 - Add blocks to emerge queue if they are not found
752 SUGGESTION: These could be ignored from the backside of the player
755 Player *player = server->m_env.getPlayer(peer_id);
759 v3f playerpos = player->getPosition();
760 v3f playerspeed = player->getSpeed();
762 v3s16 center_nodepos = floatToInt(playerpos, BS);
763 v3s16 center = getNodeBlockPos(center_nodepos);
765 s16 d_max = g_settings.getS16("active_object_range");
767 // Number of blocks whose objects were written to bos
770 std::ostringstream bos(std::ios_base::binary);
772 for(s16 d = 0; d <= d_max; d++)
774 core::list<v3s16> list;
775 getFacePositions(list, d);
777 core::list<v3s16>::Iterator li;
778 for(li=list.begin(); li!=list.end(); li++)
780 v3s16 p = *li + center;
783 Ignore blocks that haven't been sent to the client
786 if(m_blocks_sent.find(p) == NULL)
790 // Try stepping block and add it to a send queue
795 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
798 Step block if not in stepped_blocks and add to stepped_blocks.
800 if(stepped_blocks.find(p) == NULL)
802 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
803 stepped_blocks.insert(p, true);
804 block->setChangedFlag();
807 // Skip block if there are no objects
808 if(block->getObjectCount() == 0)
817 bos.write((char*)buf, 6);
820 block->serializeObjects(bos, serialization_version);
825 Stop collecting objects if data is already too big
827 // Sum of player and object data sizes
828 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
829 // break out if data too big
830 if(sum > MAX_OBJECTDATA_SIZE)
832 goto skip_subsequent;
836 catch(InvalidPositionException &e)
839 // Add it to the emerge queue and trigger the thread.
840 // Fetch the block only if it is on disk.
842 // Grab and increment counter
843 /*SharedPtr<JMutexAutoLock> lock
844 (m_num_blocks_in_emerge_queue.getLock());
845 m_num_blocks_in_emerge_queue.m_value++;*/
847 // Add to queue as an anonymous fetch from disk
848 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
849 server->m_emerge_queue.addBlock(0, p, flags);
850 server->m_emergethread.trigger();
858 writeU16(buf, blockcount);
859 os.write((char*)buf, 2);
861 // Write block objects
868 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
871 std::string s = os.str();
872 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
873 // Send as unreliable
874 server->m_con.Send(peer_id, 0, data, false);
877 void RemoteClient::GotBlock(v3s16 p)
879 if(m_blocks_sending.find(p) != NULL)
880 m_blocks_sending.remove(p);
883 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
884 " m_blocks_sending"<<std::endl;*/
885 m_excess_gotblocks++;
887 m_blocks_sent.insert(p, true);
890 void RemoteClient::SentBlock(v3s16 p)
892 if(m_blocks_sending.find(p) == NULL)
893 m_blocks_sending.insert(p, 0.0);
895 dstream<<"RemoteClient::SentBlock(): Sent block"
896 " already in m_blocks_sending"<<std::endl;
899 void RemoteClient::SetBlockNotSent(v3s16 p)
901 m_nearest_unsent_d = 0;
903 if(m_blocks_sending.find(p) != NULL)
904 m_blocks_sending.remove(p);
905 if(m_blocks_sent.find(p) != NULL)
906 m_blocks_sent.remove(p);
909 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
911 m_nearest_unsent_d = 0;
913 for(core::map<v3s16, MapBlock*>::Iterator
914 i = blocks.getIterator();
915 i.atEnd()==false; i++)
917 v3s16 p = i.getNode()->getKey();
919 if(m_blocks_sending.find(p) != NULL)
920 m_blocks_sending.remove(p);
921 if(m_blocks_sent.find(p) != NULL)
922 m_blocks_sent.remove(p);
930 PlayerInfo::PlayerInfo()
936 void PlayerInfo::PrintLine(std::ostream *s)
939 (*s)<<"\""<<name<<"\" ("
940 <<(position.X/10)<<","<<(position.Y/10)
941 <<","<<(position.Z/10)<<") ";
943 (*s)<<" avg_rtt="<<avg_rtt;
947 u32 PIChecksum(core::list<PlayerInfo> &l)
949 core::list<PlayerInfo>::Iterator i;
952 for(i=l.begin(); i!=l.end(); i++)
954 checksum += a * (i->id+1);
955 checksum ^= 0x435aafcd;
966 std::string mapsavedir
968 m_env(new ServerMap(mapsavedir), this),
969 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
971 m_emergethread(this),
973 m_time_of_day_send_timer(0),
975 m_mapsavedir(mapsavedir),
976 m_shutdown_requested(false),
977 m_ignore_map_edit_events(false),
978 m_ignore_map_edit_events_peer_id(0)
980 m_liquid_transform_timer = 0.0;
981 m_print_info_timer = 0.0;
982 m_objectdata_timer = 0.0;
983 m_emergethread_trigger_timer = 0.0;
984 m_savemap_timer = 0.0;
988 m_step_dtime_mutex.Init();
991 // Register us to receive map edit events
992 m_env.getMap().addEventReceiver(this);
994 // If file exists, load environment metadata
995 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
997 dstream<<"Server: Loading environment metadata"<<std::endl;
998 m_env.loadMeta(m_mapsavedir);
1002 dstream<<"Server: Loading players"<<std::endl;
1003 m_env.deSerializePlayers(m_mapsavedir);
1008 dstream<<"Server::~Server()"<<std::endl;
1011 Send shutdown message
1014 JMutexAutoLock conlock(m_con_mutex);
1016 std::wstring line = L"*** Server shutting down";
1019 Send the message to clients
1021 for(core::map<u16, RemoteClient*>::Iterator
1022 i = m_clients.getIterator();
1023 i.atEnd() == false; i++)
1025 // Get client and check that it is valid
1026 RemoteClient *client = i.getNode()->getValue();
1027 assert(client->peer_id == i.getNode()->getKey());
1028 if(client->serialization_version == SER_FMT_VER_INVALID)
1032 SendChatMessage(client->peer_id, line);
1034 catch(con::PeerNotFoundException &e)
1042 dstream<<"Server: Saving players"<<std::endl;
1043 m_env.serializePlayers(m_mapsavedir);
1046 Save environment metadata
1048 dstream<<"Server: Saving environment metadata"<<std::endl;
1049 m_env.saveMeta(m_mapsavedir);
1060 JMutexAutoLock clientslock(m_con_mutex);
1062 for(core::map<u16, RemoteClient*>::Iterator
1063 i = m_clients.getIterator();
1064 i.atEnd() == false; i++)
1067 // NOTE: These are removed by env destructor
1069 u16 peer_id = i.getNode()->getKey();
1070 JMutexAutoLock envlock(m_env_mutex);
1071 m_env.removePlayer(peer_id);
1075 delete i.getNode()->getValue();
1080 void Server::start(unsigned short port)
1082 DSTACK(__FUNCTION_NAME);
1083 // Stop thread if already running
1086 // Initialize connection
1087 m_con.setTimeoutMs(30);
1091 m_thread.setRun(true);
1094 dout_server<<"Server: Started on port "<<port<<std::endl;
1099 DSTACK(__FUNCTION_NAME);
1101 // Stop threads (set run=false first so both start stopping)
1102 m_thread.setRun(false);
1103 m_emergethread.setRun(false);
1105 m_emergethread.stop();
1107 dout_server<<"Server: Threads stopped"<<std::endl;
1110 void Server::step(float dtime)
1112 DSTACK(__FUNCTION_NAME);
1117 JMutexAutoLock lock(m_step_dtime_mutex);
1118 m_step_dtime += dtime;
1122 void Server::AsyncRunStep()
1124 DSTACK(__FUNCTION_NAME);
1128 JMutexAutoLock lock1(m_step_dtime_mutex);
1129 dtime = m_step_dtime;
1132 // Send blocks to clients
1138 //dstream<<"Server steps "<<dtime<<std::endl;
1139 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1142 JMutexAutoLock lock1(m_step_dtime_mutex);
1143 m_step_dtime -= dtime;
1150 m_uptime.set(m_uptime.get() + dtime);
1154 Update m_time_of_day and overall game time
1157 JMutexAutoLock envlock(m_env_mutex);
1159 m_time_counter += dtime;
1160 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1161 u32 units = (u32)(m_time_counter*speed);
1162 m_time_counter -= (f32)units / speed;
1164 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1166 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1169 Send to clients at constant intervals
1172 m_time_of_day_send_timer -= dtime;
1173 if(m_time_of_day_send_timer < 0.0)
1175 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1177 //JMutexAutoLock envlock(m_env_mutex);
1178 JMutexAutoLock conlock(m_con_mutex);
1180 for(core::map<u16, RemoteClient*>::Iterator
1181 i = m_clients.getIterator();
1182 i.atEnd() == false; i++)
1184 RemoteClient *client = i.getNode()->getValue();
1185 //Player *player = m_env.getPlayer(client->peer_id);
1187 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1188 m_env.getTimeOfDay());
1190 m_con.Send(client->peer_id, 0, data, true);
1196 // Process connection's timeouts
1197 JMutexAutoLock lock2(m_con_mutex);
1198 m_con.RunTimeouts(dtime);
1202 // This has to be called so that the client list gets synced
1203 // with the peer list of the connection
1204 handlePeerChanges();
1209 // This also runs Map's timers
1210 JMutexAutoLock lock(m_env_mutex);
1221 m_liquid_transform_timer += dtime;
1222 if(m_liquid_transform_timer >= 1.00)
1224 m_liquid_transform_timer -= 1.00;
1226 JMutexAutoLock lock(m_env_mutex);
1228 core::map<v3s16, MapBlock*> modified_blocks;
1229 m_env.getMap().transformLiquids(modified_blocks);
1234 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1235 ServerMap &map = ((ServerMap&)m_env.getMap());
1236 map.updateLighting(modified_blocks, lighting_modified_blocks);
1238 // Add blocks modified by lighting to modified_blocks
1239 for(core::map<v3s16, MapBlock*>::Iterator
1240 i = lighting_modified_blocks.getIterator();
1241 i.atEnd() == false; i++)
1243 MapBlock *block = i.getNode()->getValue();
1244 modified_blocks.insert(block->getPos(), block);
1248 Set the modified blocks unsent for all the clients
1251 JMutexAutoLock lock2(m_con_mutex);
1253 for(core::map<u16, RemoteClient*>::Iterator
1254 i = m_clients.getIterator();
1255 i.atEnd() == false; i++)
1257 RemoteClient *client = i.getNode()->getValue();
1259 if(modified_blocks.size() > 0)
1261 // Remove block from sent history
1262 client->SetBlocksNotSent(modified_blocks);
1267 // Periodically print some info
1269 float &counter = m_print_info_timer;
1275 JMutexAutoLock lock2(m_con_mutex);
1277 for(core::map<u16, RemoteClient*>::Iterator
1278 i = m_clients.getIterator();
1279 i.atEnd() == false; i++)
1281 //u16 peer_id = i.getNode()->getKey();
1282 RemoteClient *client = i.getNode()->getValue();
1283 Player *player = m_env.getPlayer(client->peer_id);
1286 std::cout<<player->getName()<<"\t";
1287 client->PrintInfo(std::cout);
1292 //if(g_settings.getBool("enable_experimental"))
1296 Check added and deleted active objects
1299 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1301 JMutexAutoLock envlock(m_env_mutex);
1302 JMutexAutoLock conlock(m_con_mutex);
1304 // Radius inside which objects are active
1307 for(core::map<u16, RemoteClient*>::Iterator
1308 i = m_clients.getIterator();
1309 i.atEnd() == false; i++)
1311 RemoteClient *client = i.getNode()->getValue();
1312 Player *player = m_env.getPlayer(client->peer_id);
1315 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1316 <<" has no associated player"<<std::endl;
1319 v3s16 pos = floatToInt(player->getPosition(), BS);
1321 core::map<u16, bool> removed_objects;
1322 core::map<u16, bool> added_objects;
1323 m_env.getRemovedActiveObjects(pos, radius,
1324 client->m_known_objects, removed_objects);
1325 m_env.getAddedActiveObjects(pos, radius,
1326 client->m_known_objects, added_objects);
1328 // Ignore if nothing happened
1329 if(removed_objects.size() == 0 && added_objects.size() == 0)
1331 //dstream<<"INFO: active objects: none changed"<<std::endl;
1335 std::string data_buffer;
1339 // Handle removed objects
1340 writeU16((u8*)buf, removed_objects.size());
1341 data_buffer.append(buf, 2);
1342 for(core::map<u16, bool>::Iterator
1343 i = removed_objects.getIterator();
1344 i.atEnd()==false; i++)
1347 u16 id = i.getNode()->getKey();
1348 ServerActiveObject* obj = m_env.getActiveObject(id);
1350 // Add to data buffer for sending
1351 writeU16((u8*)buf, i.getNode()->getKey());
1352 data_buffer.append(buf, 2);
1354 // Remove from known objects
1355 client->m_known_objects.remove(i.getNode()->getKey());
1357 if(obj && obj->m_known_by_count > 0)
1358 obj->m_known_by_count--;
1361 // Handle added objects
1362 writeU16((u8*)buf, added_objects.size());
1363 data_buffer.append(buf, 2);
1364 for(core::map<u16, bool>::Iterator
1365 i = added_objects.getIterator();
1366 i.atEnd()==false; i++)
1369 u16 id = i.getNode()->getKey();
1370 ServerActiveObject* obj = m_env.getActiveObject(id);
1373 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1375 dstream<<"WARNING: "<<__FUNCTION_NAME
1376 <<": NULL object"<<std::endl;
1378 type = obj->getType();
1380 // Add to data buffer for sending
1381 writeU16((u8*)buf, id);
1382 data_buffer.append(buf, 2);
1383 writeU8((u8*)buf, type);
1384 data_buffer.append(buf, 1);
1387 data_buffer.append(serializeLongString(
1388 obj->getClientInitializationData()));
1390 data_buffer.append(serializeLongString(""));
1392 // Add to known objects
1393 client->m_known_objects.insert(i.getNode()->getKey(), false);
1396 obj->m_known_by_count++;
1400 SharedBuffer<u8> reply(2 + data_buffer.size());
1401 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1402 memcpy((char*)&reply[2], data_buffer.c_str(),
1403 data_buffer.size());
1405 m_con.Send(client->peer_id, 0, reply, true);
1407 dstream<<"INFO: Server: Sent object remove/add: "
1408 <<removed_objects.size()<<" removed, "
1409 <<added_objects.size()<<" added, "
1410 <<"packet size is "<<reply.getSize()<<std::endl;
1415 Collect a list of all the objects known by the clients
1416 and report it back to the environment.
1419 core::map<u16, bool> all_known_objects;
1421 for(core::map<u16, RemoteClient*>::Iterator
1422 i = m_clients.getIterator();
1423 i.atEnd() == false; i++)
1425 RemoteClient *client = i.getNode()->getValue();
1426 // Go through all known objects of client
1427 for(core::map<u16, bool>::Iterator
1428 i = client->m_known_objects.getIterator();
1429 i.atEnd()==false; i++)
1431 u16 id = i.getNode()->getKey();
1432 all_known_objects[id] = true;
1436 m_env.setKnownActiveObjects(whatever);
1442 Send object messages
1445 JMutexAutoLock envlock(m_env_mutex);
1446 JMutexAutoLock conlock(m_con_mutex);
1449 // Value = data sent by object
1450 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1452 // Get active object messages from environment
1455 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1459 core::list<ActiveObjectMessage>* message_list = NULL;
1460 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1461 n = buffered_messages.find(aom.id);
1464 message_list = new core::list<ActiveObjectMessage>;
1465 buffered_messages.insert(aom.id, message_list);
1469 message_list = n->getValue();
1471 message_list->push_back(aom);
1474 // Route data to every client
1475 for(core::map<u16, RemoteClient*>::Iterator
1476 i = m_clients.getIterator();
1477 i.atEnd()==false; i++)
1479 RemoteClient *client = i.getNode()->getValue();
1480 std::string reliable_data;
1481 std::string unreliable_data;
1482 // Go through all objects in message buffer
1483 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1484 j = buffered_messages.getIterator();
1485 j.atEnd()==false; j++)
1487 // If object is not known by client, skip it
1488 u16 id = j.getNode()->getKey();
1489 if(client->m_known_objects.find(id) == NULL)
1491 // Get message list of object
1492 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1493 // Go through every message
1494 for(core::list<ActiveObjectMessage>::Iterator
1495 k = list->begin(); k != list->end(); k++)
1497 // Compose the full new data with header
1498 ActiveObjectMessage aom = *k;
1499 std::string new_data;
1502 writeU16((u8*)&buf[0], aom.id);
1503 new_data.append(buf, 2);
1505 new_data += serializeString(aom.datastring);
1506 // Add data to buffer
1508 reliable_data += new_data;
1510 unreliable_data += new_data;
1514 reliable_data and unreliable_data are now ready.
1517 if(reliable_data.size() > 0)
1519 SharedBuffer<u8> reply(2 + reliable_data.size());
1520 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1521 memcpy((char*)&reply[2], reliable_data.c_str(),
1522 reliable_data.size());
1524 m_con.Send(client->peer_id, 0, reply, true);
1526 if(unreliable_data.size() > 0)
1528 SharedBuffer<u8> reply(2 + unreliable_data.size());
1529 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1530 memcpy((char*)&reply[2], unreliable_data.c_str(),
1531 unreliable_data.size());
1532 // Send as unreliable
1533 m_con.Send(client->peer_id, 0, reply, false);
1536 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1538 dstream<<"INFO: Server: Size of object message data: "
1539 <<"reliable: "<<reliable_data.size()
1540 <<", unreliable: "<<unreliable_data.size()
1545 // Clear buffered_messages
1546 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1547 i = buffered_messages.getIterator();
1548 i.atEnd()==false; i++)
1550 delete i.getNode()->getValue();
1554 } // enable_experimental
1557 Send queued-for-sending map edit events.
1560 while(m_unsent_map_edit_queue.size() != 0)
1562 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1564 if(event->type == MEET_ADDNODE)
1566 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1567 sendAddNode(event->p, event->n, event->already_known_by_peer);
1569 else if(event->type == MEET_REMOVENODE)
1571 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1572 sendRemoveNode(event->p, event->already_known_by_peer);
1574 else if(event->type == MEET_OTHER)
1576 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1581 dstream<<"WARNING: Server: Unknown MapEditEvent "
1582 <<((u32)event->type)<<std::endl;
1590 Send object positions
1591 TODO: Get rid of MapBlockObjects
1594 float &counter = m_objectdata_timer;
1596 if(counter >= g_settings.getFloat("objectdata_interval"))
1598 JMutexAutoLock lock1(m_env_mutex);
1599 JMutexAutoLock lock2(m_con_mutex);
1600 SendObjectData(counter);
1610 //TimeTaker timer("Step node metadata");
1612 JMutexAutoLock envlock(m_env_mutex);
1613 JMutexAutoLock conlock(m_con_mutex);
1615 core::map<v3s16, MapBlock*> changed_blocks;
1616 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1618 for(core::map<v3s16, MapBlock*>::Iterator
1619 i = changed_blocks.getIterator();
1620 i.atEnd() == false; i++)
1622 MapBlock *block = i.getNode()->getValue();
1624 for(core::map<u16, RemoteClient*>::Iterator
1625 i = m_clients.getIterator();
1626 i.atEnd()==false; i++)
1628 RemoteClient *client = i.getNode()->getValue();
1629 client->SetBlockNotSent(block->getPos());
1635 Trigger emergethread (it somehow gets to a non-triggered but
1636 bysy state sometimes)
1639 float &counter = m_emergethread_trigger_timer;
1645 m_emergethread.trigger();
1651 float &counter = m_savemap_timer;
1653 if(counter >= g_settings.getFloat("server_map_save_interval"))
1657 JMutexAutoLock lock(m_env_mutex);
1659 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1661 // Save only changed parts
1662 m_env.getMap().save(true);
1664 // Delete unused sectors
1665 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1666 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1667 if(deleted_count > 0)
1669 dout_server<<"Server: Unloaded "<<deleted_count
1670 <<" sectors from memory"<<std::endl;
1674 m_env.serializePlayers(m_mapsavedir);
1676 // Save environment metadata
1677 m_env.saveMeta(m_mapsavedir);
1683 void Server::Receive()
1685 DSTACK(__FUNCTION_NAME);
1686 u32 data_maxsize = 10000;
1687 Buffer<u8> data(data_maxsize);
1692 JMutexAutoLock conlock(m_con_mutex);
1693 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1696 // This has to be called so that the client list gets synced
1697 // with the peer list of the connection
1698 handlePeerChanges();
1700 ProcessData(*data, datasize, peer_id);
1702 catch(con::InvalidIncomingDataException &e)
1704 derr_server<<"Server::Receive(): "
1705 "InvalidIncomingDataException: what()="
1706 <<e.what()<<std::endl;
1708 catch(con::PeerNotFoundException &e)
1710 //NOTE: This is not needed anymore
1712 // The peer has been disconnected.
1713 // Find the associated player and remove it.
1715 /*JMutexAutoLock envlock(m_env_mutex);
1717 dout_server<<"ServerThread: peer_id="<<peer_id
1718 <<" has apparently closed connection. "
1719 <<"Removing player."<<std::endl;
1721 m_env.removePlayer(peer_id);*/
1725 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1727 DSTACK(__FUNCTION_NAME);
1728 // Environment is locked first.
1729 JMutexAutoLock envlock(m_env_mutex);
1730 JMutexAutoLock conlock(m_con_mutex);
1734 peer = m_con.GetPeer(peer_id);
1736 catch(con::PeerNotFoundException &e)
1738 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1739 <<peer_id<<" not found"<<std::endl;
1743 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1751 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1753 if(command == TOSERVER_INIT)
1755 // [0] u16 TOSERVER_INIT
1756 // [2] u8 SER_FMT_VER_HIGHEST
1757 // [3] u8[20] player_name
1758 // [23] u8[28] password <--- can be sent without this, from old versions
1760 if(datasize < 2+1+PLAYERNAME_SIZE)
1763 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1764 <<peer->id<<std::endl;
1766 // First byte after command is maximum supported
1767 // serialization version
1768 u8 client_max = data[2];
1769 u8 our_max = SER_FMT_VER_HIGHEST;
1770 // Use the highest version supported by both
1771 u8 deployed = core::min_(client_max, our_max);
1772 // If it's lower than the lowest supported, give up.
1773 if(deployed < SER_FMT_VER_LOWEST)
1774 deployed = SER_FMT_VER_INVALID;
1776 //peer->serialization_version = deployed;
1777 getClient(peer->id)->pending_serialization_version = deployed;
1779 if(deployed == SER_FMT_VER_INVALID)
1781 derr_server<<DTIME<<"Server: Cannot negotiate "
1782 "serialization version with peer "
1783 <<peer_id<<std::endl;
1792 char playername[PLAYERNAME_SIZE];
1793 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1795 playername[i] = data[3+i];
1797 playername[PLAYERNAME_SIZE-1] = 0;
1800 char password[PASSWORD_SIZE];
1801 if(datasize == 2+1+PLAYERNAME_SIZE)
1803 // old version - assume blank password
1808 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1810 password[i] = data[23+i];
1812 password[PASSWORD_SIZE-1] = 0;
1814 Player *checkplayer = m_env.getPlayer(playername);
1815 if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
1817 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1818 <<": supplied invalid password for "
1819 <<playername<<std::endl;
1820 SendAccessDenied(m_con, peer_id);
1825 Player *player = emergePlayer(playername, password, peer_id);
1829 // DEBUG: Test serialization
1830 std::ostringstream test_os;
1831 player->serialize(test_os);
1832 dstream<<"Player serialization test: \""<<test_os.str()
1834 std::istringstream test_is(test_os.str());
1835 player->deSerialize(test_is);
1838 // If failed, cancel
1841 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1842 <<": failed to emerge player"<<std::endl;
1847 // If a client is already connected to the player, cancel
1848 if(player->peer_id != 0)
1850 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1851 <<" tried to connect to "
1852 "an already connected player (peer_id="
1853 <<player->peer_id<<")"<<std::endl;
1856 // Set client of player
1857 player->peer_id = peer_id;
1860 // Check if player doesn't exist
1862 throw con::InvalidIncomingDataException
1863 ("Server::ProcessData(): INIT: Player doesn't exist");
1865 /*// update name if it was supplied
1866 if(datasize >= 20+3)
1869 player->updateName((const char*)&data[3]);
1873 Answer with a TOCLIENT_INIT
1876 SharedBuffer<u8> reply(2+1+6+8);
1877 writeU16(&reply[0], TOCLIENT_INIT);
1878 writeU8(&reply[2], deployed);
1879 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1880 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1881 writeU64(&reply[2+1+6], 0); // no seed
1884 m_con.Send(peer_id, 0, reply, true);
1888 Send complete position information
1890 SendMovePlayer(player);
1895 if(command == TOSERVER_INIT2)
1897 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1898 <<peer->id<<std::endl;
1901 getClient(peer->id)->serialization_version
1902 = getClient(peer->id)->pending_serialization_version;
1905 Send some initialization data
1908 // Send player info to all players
1911 // Send inventory to player
1912 UpdateCrafting(peer->id);
1913 SendInventory(peer->id);
1917 Player *player = m_env.getPlayer(peer_id);
1918 SendPlayerHP(player);
1923 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1924 m_env.getTimeOfDay());
1925 m_con.Send(peer->id, 0, data, true);
1928 // Send information about server to player in chat
1929 SendChatMessage(peer_id, getStatusString());
1931 // Send information about joining in chat
1933 std::wstring name = L"unknown";
1934 Player *player = m_env.getPlayer(peer_id);
1936 name = narrow_to_wide(player->getName());
1938 std::wstring message;
1941 message += L" joined game";
1942 BroadcastChatMessage(message);
1948 if(peer_ser_ver == SER_FMT_VER_INVALID)
1950 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1951 " serialization format invalid or not initialized."
1952 " Skipping incoming command="<<command<<std::endl;
1956 Player *player = m_env.getPlayer(peer_id);
1959 derr_server<<"Server::ProcessData(): Cancelling: "
1960 "No player for peer_id="<<peer_id
1964 if(command == TOSERVER_PLAYERPOS)
1966 if(datasize < 2+12+12+4+4)
1970 v3s32 ps = readV3S32(&data[start+2]);
1971 v3s32 ss = readV3S32(&data[start+2+12]);
1972 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1973 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1974 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1975 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1976 pitch = wrapDegrees(pitch);
1977 yaw = wrapDegrees(yaw);
1978 player->setPosition(position);
1979 player->setSpeed(speed);
1980 player->setPitch(pitch);
1981 player->setYaw(yaw);
1983 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1984 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1985 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1987 else if(command == TOSERVER_GOTBLOCKS)
2000 u16 count = data[2];
2001 for(u16 i=0; i<count; i++)
2003 if((s16)datasize < 2+1+(i+1)*6)
2004 throw con::InvalidIncomingDataException
2005 ("GOTBLOCKS length is too short");
2006 v3s16 p = readV3S16(&data[2+1+i*6]);
2007 /*dstream<<"Server: GOTBLOCKS ("
2008 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2009 RemoteClient *client = getClient(peer_id);
2010 client->GotBlock(p);
2013 else if(command == TOSERVER_DELETEDBLOCKS)
2026 u16 count = data[2];
2027 for(u16 i=0; i<count; i++)
2029 if((s16)datasize < 2+1+(i+1)*6)
2030 throw con::InvalidIncomingDataException
2031 ("DELETEDBLOCKS length is too short");
2032 v3s16 p = readV3S16(&data[2+1+i*6]);
2033 /*dstream<<"Server: DELETEDBLOCKS ("
2034 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2035 RemoteClient *client = getClient(peer_id);
2036 client->SetBlockNotSent(p);
2039 else if(command == TOSERVER_CLICK_OBJECT)
2044 if((player->privs & PRIV_BUILD) == 0)
2049 [2] u8 button (0=left, 1=right)
2054 u8 button = readU8(&data[2]);
2056 p.X = readS16(&data[3]);
2057 p.Y = readS16(&data[5]);
2058 p.Z = readS16(&data[7]);
2059 s16 id = readS16(&data[9]);
2060 //u16 item_i = readU16(&data[11]);
2062 MapBlock *block = NULL;
2065 block = m_env.getMap().getBlockNoCreate(p);
2067 catch(InvalidPositionException &e)
2069 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2073 MapBlockObject *obj = block->getObject(id);
2077 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2081 //TODO: Check that object is reasonably close
2086 InventoryList *ilist = player->inventory.getList("main");
2087 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2090 // Skip if inventory has no free space
2091 if(ilist->getUsedSlots() == ilist->getSize())
2093 dout_server<<"Player inventory has no free space"<<std::endl;
2098 Create the inventory item
2100 InventoryItem *item = NULL;
2101 // If it is an item-object, take the item from it
2102 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2104 item = ((ItemObject*)obj)->createInventoryItem();
2106 // Else create an item of the object
2109 item = new MapBlockObjectItem
2110 (obj->getInventoryString());
2113 // Add to inventory and send inventory
2114 ilist->addItem(item);
2115 UpdateCrafting(player->peer_id);
2116 SendInventory(player->peer_id);
2119 // Remove from block
2120 block->removeObject(id);
2123 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2128 if((player->privs & PRIV_BUILD) == 0)
2134 [2] u8 button (0=left, 1=right)
2138 u8 button = readU8(&data[2]);
2139 u16 id = readS16(&data[3]);
2140 u16 item_i = readU16(&data[11]);
2142 ServerActiveObject *obj = m_env.getActiveObject(id);
2146 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2151 //TODO: Check that object is reasonably close
2153 // Left click, pick object up (usually)
2156 InventoryList *ilist = player->inventory.getList("main");
2157 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2160 // Skip if inventory has no free space
2161 if(ilist->getUsedSlots() == ilist->getSize())
2163 dout_server<<"Player inventory has no free space"<<std::endl;
2167 // Skip if object has been removed
2172 Create the inventory item
2174 InventoryItem *item = obj->createPickedUpItem();
2178 // Add to inventory and send inventory
2179 ilist->addItem(item);
2180 UpdateCrafting(player->peer_id);
2181 SendInventory(player->peer_id);
2183 // Remove object from environment
2184 obj->m_removed = true;
2189 Item cannot be picked up. Punch it instead.
2192 ToolItem *titem = NULL;
2193 std::string toolname = "";
2195 InventoryList *mlist = player->inventory.getList("main");
2198 InventoryItem *item = mlist->getItem(item_i);
2199 if(item && (std::string)item->getName() == "ToolItem")
2201 titem = (ToolItem*)item;
2202 toolname = titem->getToolName();
2206 u16 wear = obj->punch(toolname);
2210 bool weared_out = titem->addWear(wear);
2212 mlist->deleteItem(item_i);
2213 SendInventory(player->peer_id);
2219 else if(command == TOSERVER_GROUND_ACTION)
2227 [3] v3s16 nodepos_undersurface
2228 [9] v3s16 nodepos_abovesurface
2233 2: stop digging (all parameters ignored)
2234 3: digging completed
2236 u8 action = readU8(&data[2]);
2238 p_under.X = readS16(&data[3]);
2239 p_under.Y = readS16(&data[5]);
2240 p_under.Z = readS16(&data[7]);
2242 p_over.X = readS16(&data[9]);
2243 p_over.Y = readS16(&data[11]);
2244 p_over.Z = readS16(&data[13]);
2245 u16 item_i = readU16(&data[15]);
2247 //TODO: Check that target is reasonably close
2255 NOTE: This can be used in the future to check if
2256 somebody is cheating, by checking the timing.
2263 else if(action == 2)
2266 RemoteClient *client = getClient(peer->id);
2267 JMutexAutoLock digmutex(client->m_dig_mutex);
2268 client->m_dig_tool_item = -1;
2273 3: Digging completed
2275 else if(action == 3)
2277 // Mandatory parameter; actually used for nothing
2278 core::map<v3s16, MapBlock*> modified_blocks;
2280 u8 material = CONTENT_IGNORE;
2281 u8 mineral = MINERAL_NONE;
2283 bool cannot_remove_node = false;
2287 MapNode n = m_env.getMap().getNode(p_under);
2289 mineral = n.getMineral();
2290 // Get material at position
2292 // If not yet cancelled
2293 if(cannot_remove_node == false)
2295 // If it's not diggable, do nothing
2296 if(content_diggable(material) == false)
2298 derr_server<<"Server: Not finishing digging: "
2299 <<"Node not diggable"
2301 cannot_remove_node = true;
2304 // If not yet cancelled
2305 if(cannot_remove_node == false)
2307 // Get node metadata
2308 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2309 if(meta && meta->nodeRemovalDisabled() == true)
2311 derr_server<<"Server: Not finishing digging: "
2312 <<"Node metadata disables removal"
2314 cannot_remove_node = true;
2318 catch(InvalidPositionException &e)
2320 derr_server<<"Server: Not finishing digging: Node not found."
2321 <<" Adding block to emerge queue."
2323 m_emerge_queue.addBlock(peer_id,
2324 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2325 cannot_remove_node = true;
2328 // Make sure the player is allowed to do it
2329 if((player->privs & PRIV_BUILD) == 0)
2330 cannot_remove_node = true;
2333 If node can't be removed, set block to be re-sent to
2336 if(cannot_remove_node)
2338 derr_server<<"Server: Not finishing digging."<<std::endl;
2340 // Client probably has wrong data.
2341 // Set block not sent, so that client will get
2343 dstream<<"Client "<<peer_id<<" tried to dig "
2344 <<"node; but node cannot be removed."
2345 <<" setting MapBlock not sent."<<std::endl;
2346 RemoteClient *client = getClient(peer_id);
2347 v3s16 blockpos = getNodeBlockPos(p_under);
2348 client->SetBlockNotSent(blockpos);
2354 Send the removal to all other clients.
2355 - If other player is close, send REMOVENODE
2356 - Otherwise set blocks not sent
2358 core::list<u16> far_players;
2359 sendRemoveNode(p_under, peer_id, &far_players, 100);
2362 Update and send inventory
2365 if(g_settings.getBool("creative_mode") == false)
2370 InventoryList *mlist = player->inventory.getList("main");
2373 InventoryItem *item = mlist->getItem(item_i);
2374 if(item && (std::string)item->getName() == "ToolItem")
2376 ToolItem *titem = (ToolItem*)item;
2377 std::string toolname = titem->getToolName();
2379 // Get digging properties for material and tool
2380 DiggingProperties prop =
2381 getDiggingProperties(material, toolname);
2383 if(prop.diggable == false)
2385 derr_server<<"Server: WARNING: Player digged"
2386 <<" with impossible material + tool"
2387 <<" combination"<<std::endl;
2390 bool weared_out = titem->addWear(prop.wear);
2394 mlist->deleteItem(item_i);
2400 Add dug item to inventory
2403 InventoryItem *item = NULL;
2405 if(mineral != MINERAL_NONE)
2406 item = getDiggedMineralItem(mineral);
2411 std::string &dug_s = content_features(material).dug_item;
2414 std::istringstream is(dug_s, std::ios::binary);
2415 item = InventoryItem::deSerialize(is);
2421 // Add a item to inventory
2422 player->inventory.addItem("main", item);
2425 UpdateCrafting(player->peer_id);
2426 SendInventory(player->peer_id);
2432 (this takes some time so it is done after the quick stuff)
2434 m_ignore_map_edit_events = true;
2435 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2436 m_ignore_map_edit_events = false;
2439 Set blocks not sent to far players
2441 for(core::list<u16>::Iterator
2442 i = far_players.begin();
2443 i != far_players.end(); i++)
2446 RemoteClient *client = getClient(peer_id);
2449 client->SetBlocksNotSent(modified_blocks);
2456 else if(action == 1)
2459 InventoryList *ilist = player->inventory.getList("main");
2464 InventoryItem *item = ilist->getItem(item_i);
2466 // If there is no item, it is not possible to add it anywhere
2471 Handle material items
2473 if(std::string("MaterialItem") == item->getName())
2476 // Don't add a node if this is not a free space
2477 MapNode n2 = m_env.getMap().getNode(p_over);
2478 if(content_buildable_to(n2.d) == false
2479 || (player->privs & PRIV_BUILD) ==0)
2481 // Client probably has wrong data.
2482 // Set block not sent, so that client will get
2484 dstream<<"Client "<<peer_id<<" tried to place"
2485 <<" node in invalid position; setting"
2486 <<" MapBlock not sent."<<std::endl;
2487 RemoteClient *client = getClient(peer_id);
2488 v3s16 blockpos = getNodeBlockPos(p_over);
2489 client->SetBlockNotSent(blockpos);
2493 catch(InvalidPositionException &e)
2495 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2496 <<" Adding block to emerge queue."
2498 m_emerge_queue.addBlock(peer_id,
2499 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2503 // Reset build time counter
2504 getClient(peer->id)->m_time_from_building = 0.0;
2507 MaterialItem *mitem = (MaterialItem*)item;
2509 n.d = mitem->getMaterial();
2510 if(content_features(n.d).wall_mounted)
2511 n.dir = packDir(p_under - p_over);
2516 core::list<u16> far_players;
2517 sendAddNode(p_over, n, 0, &far_players, 100);
2522 InventoryList *ilist = player->inventory.getList("main");
2523 if(g_settings.getBool("creative_mode") == false && ilist)
2525 // Remove from inventory and send inventory
2526 if(mitem->getCount() == 1)
2527 ilist->deleteItem(item_i);
2531 UpdateCrafting(peer_id);
2532 SendInventory(peer_id);
2538 This takes some time so it is done after the quick stuff
2540 core::map<v3s16, MapBlock*> modified_blocks;
2541 m_ignore_map_edit_events = true;
2542 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2543 m_ignore_map_edit_events = false;
2546 Set blocks not sent to far players
2548 for(core::list<u16>::Iterator
2549 i = far_players.begin();
2550 i != far_players.end(); i++)
2553 RemoteClient *client = getClient(peer_id);
2556 client->SetBlocksNotSent(modified_blocks);
2560 Calculate special events
2563 /*if(n.d == CONTENT_MESE)
2566 for(s16 z=-1; z<=1; z++)
2567 for(s16 y=-1; y<=1; y++)
2568 for(s16 x=-1; x<=1; x++)
2575 Place other item (not a block)
2579 v3s16 blockpos = getNodeBlockPos(p_over);
2582 Check that the block is loaded so that the item
2583 can properly be added to the static list too
2585 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2588 derr_server<<"Error while placing object: "
2589 "block not found"<<std::endl;
2593 dout_server<<"Placing a miscellaneous item on map"
2596 // Calculate a position for it
2597 v3f pos = intToFloat(p_over, BS);
2599 pos.Y -= BS*0.25; // let it drop a bit
2601 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2602 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2607 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2611 derr_server<<"WARNING: item resulted in NULL object, "
2612 <<"not placing onto map"
2617 // Add the object to the environment
2618 m_env.addActiveObject(obj);
2620 dout_server<<"Placed object"<<std::endl;
2622 if(g_settings.getBool("creative_mode") == false)
2624 // Delete the right amount of items from the slot
2625 u16 dropcount = item->getDropCount();
2627 // Delete item if all gone
2628 if(item->getCount() <= dropcount)
2630 if(item->getCount() < dropcount)
2631 dstream<<"WARNING: Server: dropped more items"
2632 <<" than the slot contains"<<std::endl;
2634 InventoryList *ilist = player->inventory.getList("main");
2636 // Remove from inventory and send inventory
2637 ilist->deleteItem(item_i);
2639 // Else decrement it
2641 item->remove(dropcount);
2644 UpdateCrafting(peer_id);
2645 SendInventory(peer_id);
2653 Catch invalid actions
2657 derr_server<<"WARNING: Server: Invalid action "
2658 <<action<<std::endl;
2662 else if(command == TOSERVER_RELEASE)
2671 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2674 else if(command == TOSERVER_SIGNTEXT)
2676 if((player->privs & PRIV_BUILD) == 0)
2685 std::string datastring((char*)&data[2], datasize-2);
2686 std::istringstream is(datastring, std::ios_base::binary);
2689 is.read((char*)buf, 6);
2690 v3s16 blockpos = readV3S16(buf);
2691 is.read((char*)buf, 2);
2692 s16 id = readS16(buf);
2693 is.read((char*)buf, 2);
2694 u16 textlen = readU16(buf);
2696 for(u16 i=0; i<textlen; i++)
2698 is.read((char*)buf, 1);
2699 text += (char)buf[0];
2702 MapBlock *block = NULL;
2705 block = m_env.getMap().getBlockNoCreate(blockpos);
2707 catch(InvalidPositionException &e)
2709 derr_server<<"Error while setting sign text: "
2710 "block not found"<<std::endl;
2714 MapBlockObject *obj = block->getObject(id);
2717 derr_server<<"Error while setting sign text: "
2718 "object not found"<<std::endl;
2722 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2724 derr_server<<"Error while setting sign text: "
2725 "object is not a sign"<<std::endl;
2729 ((SignObject*)obj)->setText(text);
2731 obj->getBlock()->setChangedFlag();
2733 else if(command == TOSERVER_SIGNNODETEXT)
2735 if((player->privs & PRIV_BUILD) == 0)
2743 std::string datastring((char*)&data[2], datasize-2);
2744 std::istringstream is(datastring, std::ios_base::binary);
2747 is.read((char*)buf, 6);
2748 v3s16 p = readV3S16(buf);
2749 is.read((char*)buf, 2);
2750 u16 textlen = readU16(buf);
2752 for(u16 i=0; i<textlen; i++)
2754 is.read((char*)buf, 1);
2755 text += (char)buf[0];
2758 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2761 if(meta->typeId() != CONTENT_SIGN_WALL)
2763 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2764 signmeta->setText(text);
2766 v3s16 blockpos = getNodeBlockPos(p);
2767 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2770 block->setChangedFlag();
2773 for(core::map<u16, RemoteClient*>::Iterator
2774 i = m_clients.getIterator();
2775 i.atEnd()==false; i++)
2777 RemoteClient *client = i.getNode()->getValue();
2778 client->SetBlockNotSent(blockpos);
2781 else if(command == TOSERVER_INVENTORY_ACTION)
2783 /*// Ignore inventory changes if in creative mode
2784 if(g_settings.getBool("creative_mode") == true)
2786 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2790 // Strip command and create a stream
2791 std::string datastring((char*)&data[2], datasize-2);
2792 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2793 std::istringstream is(datastring, std::ios_base::binary);
2795 InventoryAction *a = InventoryAction::deSerialize(is);
2800 c.current_player = player;
2803 Handle craftresult specially if not in creative mode
2805 bool disable_action = false;
2806 if(a->getType() == IACTION_MOVE
2807 && g_settings.getBool("creative_mode") == false)
2809 IMoveAction *ma = (IMoveAction*)a;
2810 if(ma->to_inv == "current_player" &&
2811 ma->from_inv == "current_player")
2813 InventoryList *rlist = player->inventory.getList("craftresult");
2815 InventoryList *clist = player->inventory.getList("craft");
2817 InventoryList *mlist = player->inventory.getList("main");
2820 Craftresult is no longer preview if something
2823 if(ma->to_list == "craftresult"
2824 && ma->from_list != "craftresult")
2826 // If it currently is a preview, remove
2828 if(player->craftresult_is_preview)
2830 rlist->deleteItem(0);
2832 player->craftresult_is_preview = false;
2835 Crafting takes place if this condition is true.
2837 if(player->craftresult_is_preview &&
2838 ma->from_list == "craftresult")
2840 player->craftresult_is_preview = false;
2841 clist->decrementMaterials(1);
2844 If the craftresult is placed on itself, move it to
2845 main inventory instead of doing the action
2847 if(ma->to_list == "craftresult"
2848 && ma->from_list == "craftresult")
2850 disable_action = true;
2852 InventoryItem *item1 = rlist->changeItem(0, NULL);
2853 mlist->addItem(item1);
2858 if(disable_action == false)
2860 // Feed action to player inventory
2868 UpdateCrafting(player->peer_id);
2869 SendInventory(player->peer_id);
2874 dstream<<"TOSERVER_INVENTORY_ACTION: "
2875 <<"InventoryAction::deSerialize() returned NULL"
2879 else if(command == TOSERVER_CHAT_MESSAGE)
2887 std::string datastring((char*)&data[2], datasize-2);
2888 std::istringstream is(datastring, std::ios_base::binary);
2891 is.read((char*)buf, 2);
2892 u16 len = readU16(buf);
2894 std::wstring message;
2895 for(u16 i=0; i<len; i++)
2897 is.read((char*)buf, 2);
2898 message += (wchar_t)readU16(buf);
2901 // Get player name of this client
2902 std::wstring name = narrow_to_wide(player->getName());
2904 // Line to send to players
2906 // Whether to send to the player that sent the line
2907 bool send_to_sender = false;
2908 // Whether to send to other players
2909 bool send_to_others = false;
2912 std::wstring commandprefix = L"/#";
2913 if(message.substr(0, commandprefix.size()) == commandprefix)
2915 line += L"Server: ";
2917 message = message.substr(commandprefix.size());
2919 // Local player gets all privileges regardless of
2920 // what's set on their account.
2921 u64 privs = player->privs;
2922 if(g_settings.get("name") == player->getName())
2925 ServerCommandContext *ctx = new ServerCommandContext(
2926 str_split(message, L' '),
2932 line += processServerCommand(ctx);
2933 send_to_sender = ctx->flags & 1;
2934 send_to_others = ctx->flags & 2;
2946 send_to_others = true;
2951 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2954 Send the message to clients
2956 for(core::map<u16, RemoteClient*>::Iterator
2957 i = m_clients.getIterator();
2958 i.atEnd() == false; i++)
2960 // Get client and check that it is valid
2961 RemoteClient *client = i.getNode()->getValue();
2962 assert(client->peer_id == i.getNode()->getKey());
2963 if(client->serialization_version == SER_FMT_VER_INVALID)
2967 bool sender_selected = (peer_id == client->peer_id);
2968 if(sender_selected == true && send_to_sender == false)
2970 if(sender_selected == false && send_to_others == false)
2973 SendChatMessage(client->peer_id, line);
2977 else if(command == TOSERVER_DAMAGE)
2979 if(g_settings.getBool("enable_damage"))
2981 std::string datastring((char*)&data[2], datasize-2);
2982 std::istringstream is(datastring, std::ios_base::binary);
2983 u8 damage = readU8(is);
2984 if(player->hp > damage)
2986 player->hp -= damage;
2992 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2995 v3f pos = findSpawnPos(m_env.getServerMap());
2996 player->setPosition(pos);
2998 SendMovePlayer(player);
2999 SendPlayerHP(player);
3001 //TODO: Throw items around
3005 SendPlayerHP(player);
3009 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3010 "unknown command "<<command<<std::endl;
3014 catch(SendFailedException &e)
3016 derr_server<<"Server::ProcessData(): SendFailedException: "
3022 void Server::onMapEditEvent(MapEditEvent *event)
3024 dstream<<"Server::onMapEditEvent()"<<std::endl;
3025 if(m_ignore_map_edit_events)
3027 MapEditEvent *e = event->clone();
3028 m_unsent_map_edit_queue.push_back(e);
3031 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3033 if(id == "current_player")
3035 assert(c->current_player);
3036 return &(c->current_player->inventory);
3040 std::string id0 = fn.next(":");
3042 if(id0 == "nodemeta")
3045 p.X = stoi(fn.next(","));
3046 p.Y = stoi(fn.next(","));
3047 p.Z = stoi(fn.next(","));
3048 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3050 return meta->getInventory();
3051 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3052 <<"no metadata found"<<std::endl;
3056 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3059 void Server::inventoryModified(InventoryContext *c, std::string id)
3061 if(id == "current_player")
3063 assert(c->current_player);
3065 UpdateCrafting(c->current_player->peer_id);
3066 SendInventory(c->current_player->peer_id);
3071 std::string id0 = fn.next(":");
3073 if(id0 == "nodemeta")
3076 p.X = stoi(fn.next(","));
3077 p.Y = stoi(fn.next(","));
3078 p.Z = stoi(fn.next(","));
3079 v3s16 blockpos = getNodeBlockPos(p);
3081 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3083 meta->inventoryModified();
3085 for(core::map<u16, RemoteClient*>::Iterator
3086 i = m_clients.getIterator();
3087 i.atEnd()==false; i++)
3089 RemoteClient *client = i.getNode()->getValue();
3090 client->SetBlockNotSent(blockpos);
3096 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3099 core::list<PlayerInfo> Server::getPlayerInfo()
3101 DSTACK(__FUNCTION_NAME);
3102 JMutexAutoLock envlock(m_env_mutex);
3103 JMutexAutoLock conlock(m_con_mutex);
3105 core::list<PlayerInfo> list;
3107 core::list<Player*> players = m_env.getPlayers();
3109 core::list<Player*>::Iterator i;
3110 for(i = players.begin();
3111 i != players.end(); i++)
3115 Player *player = *i;
3118 con::Peer *peer = m_con.GetPeer(player->peer_id);
3119 // Copy info from peer to info struct
3121 info.address = peer->address;
3122 info.avg_rtt = peer->avg_rtt;
3124 catch(con::PeerNotFoundException &e)
3126 // Set dummy peer info
3128 info.address = Address(0,0,0,0,0);
3132 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3133 info.position = player->getPosition();
3135 list.push_back(info);
3142 void Server::peerAdded(con::Peer *peer)
3144 DSTACK(__FUNCTION_NAME);
3145 dout_server<<"Server::peerAdded(): peer->id="
3146 <<peer->id<<std::endl;
3149 c.type = PEER_ADDED;
3150 c.peer_id = peer->id;
3152 m_peer_change_queue.push_back(c);
3155 void Server::deletingPeer(con::Peer *peer, bool timeout)
3157 DSTACK(__FUNCTION_NAME);
3158 dout_server<<"Server::deletingPeer(): peer->id="
3159 <<peer->id<<", timeout="<<timeout<<std::endl;
3162 c.type = PEER_REMOVED;
3163 c.peer_id = peer->id;
3164 c.timeout = timeout;
3165 m_peer_change_queue.push_back(c);
3172 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3174 DSTACK(__FUNCTION_NAME);
3175 std::ostringstream os(std::ios_base::binary);
3177 writeU16(os, TOCLIENT_HP);
3181 std::string s = os.str();
3182 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3184 con.Send(peer_id, 0, data, true);
3187 void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
3189 DSTACK(__FUNCTION_NAME);
3190 std::ostringstream os(std::ios_base::binary);
3192 writeU16(os, TOCLIENT_ACCESS_DENIED);
3195 std::string s = os.str();
3196 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3198 con.Send(peer_id, 0, data, true);
3202 Non-static send methods
3205 void Server::SendObjectData(float dtime)
3207 DSTACK(__FUNCTION_NAME);
3209 core::map<v3s16, bool> stepped_blocks;
3211 for(core::map<u16, RemoteClient*>::Iterator
3212 i = m_clients.getIterator();
3213 i.atEnd() == false; i++)
3215 u16 peer_id = i.getNode()->getKey();
3216 RemoteClient *client = i.getNode()->getValue();
3217 assert(client->peer_id == peer_id);
3219 if(client->serialization_version == SER_FMT_VER_INVALID)
3222 client->SendObjectData(this, dtime, stepped_blocks);
3226 void Server::SendPlayerInfos()
3228 DSTACK(__FUNCTION_NAME);
3230 //JMutexAutoLock envlock(m_env_mutex);
3232 // Get connected players
3233 core::list<Player*> players = m_env.getPlayers(true);
3235 u32 player_count = players.getSize();
3236 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3238 SharedBuffer<u8> data(datasize);
3239 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3242 core::list<Player*>::Iterator i;
3243 for(i = players.begin();
3244 i != players.end(); i++)
3246 Player *player = *i;
3248 /*dstream<<"Server sending player info for player with "
3249 "peer_id="<<player->peer_id<<std::endl;*/
3251 writeU16(&data[start], player->peer_id);
3252 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3253 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3254 start += 2+PLAYERNAME_SIZE;
3257 //JMutexAutoLock conlock(m_con_mutex);
3260 m_con.SendToAll(0, data, true);
3263 void Server::SendInventory(u16 peer_id)
3265 DSTACK(__FUNCTION_NAME);
3267 Player* player = m_env.getPlayer(peer_id);
3274 std::ostringstream os;
3275 //os.imbue(std::locale("C"));
3277 player->inventory.serialize(os);
3279 std::string s = os.str();
3281 SharedBuffer<u8> data(s.size()+2);
3282 writeU16(&data[0], TOCLIENT_INVENTORY);
3283 memcpy(&data[2], s.c_str(), s.size());
3286 m_con.Send(peer_id, 0, data, true);
3289 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3291 DSTACK(__FUNCTION_NAME);
3293 std::ostringstream os(std::ios_base::binary);
3297 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3298 os.write((char*)buf, 2);
3301 writeU16(buf, message.size());
3302 os.write((char*)buf, 2);
3305 for(u32 i=0; i<message.size(); i++)
3309 os.write((char*)buf, 2);
3313 std::string s = os.str();
3314 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3316 m_con.Send(peer_id, 0, data, true);
3319 void Server::BroadcastChatMessage(const std::wstring &message)
3321 for(core::map<u16, RemoteClient*>::Iterator
3322 i = m_clients.getIterator();
3323 i.atEnd() == false; i++)
3325 // Get client and check that it is valid
3326 RemoteClient *client = i.getNode()->getValue();
3327 assert(client->peer_id == i.getNode()->getKey());
3328 if(client->serialization_version == SER_FMT_VER_INVALID)
3331 SendChatMessage(client->peer_id, message);
3335 void Server::SendPlayerHP(Player *player)
3337 SendHP(m_con, player->peer_id, player->hp);
3340 void Server::SendMovePlayer(Player *player)
3342 DSTACK(__FUNCTION_NAME);
3343 std::ostringstream os(std::ios_base::binary);
3345 writeU16(os, TOCLIENT_MOVE_PLAYER);
3346 writeV3F1000(os, player->getPosition());
3347 writeF1000(os, player->getPitch());
3348 writeF1000(os, player->getYaw());
3351 v3f pos = player->getPosition();
3352 f32 pitch = player->getPitch();
3353 f32 yaw = player->getYaw();
3354 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3355 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3362 std::string s = os.str();
3363 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3365 m_con.Send(player->peer_id, 0, data, true);
3368 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3369 core::list<u16> *far_players, float far_d_nodes)
3371 float maxd = far_d_nodes*BS;
3372 v3f p_f = intToFloat(p, BS);
3376 SharedBuffer<u8> reply(replysize);
3377 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3378 writeS16(&reply[2], p.X);
3379 writeS16(&reply[4], p.Y);
3380 writeS16(&reply[6], p.Z);
3382 for(core::map<u16, RemoteClient*>::Iterator
3383 i = m_clients.getIterator();
3384 i.atEnd() == false; i++)
3386 // Get client and check that it is valid
3387 RemoteClient *client = i.getNode()->getValue();
3388 assert(client->peer_id == i.getNode()->getKey());
3389 if(client->serialization_version == SER_FMT_VER_INVALID)
3392 // Don't send if it's the same one
3393 if(client->peer_id == ignore_id)
3399 Player *player = m_env.getPlayer(client->peer_id);
3402 // If player is far away, only set modified blocks not sent
3403 v3f player_pos = player->getPosition();
3404 if(player_pos.getDistanceFrom(p_f) > maxd)
3406 far_players->push_back(client->peer_id);
3413 m_con.Send(client->peer_id, 0, reply, true);
3417 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3418 core::list<u16> *far_players, float far_d_nodes)
3420 float maxd = far_d_nodes*BS;
3421 v3f p_f = intToFloat(p, BS);
3423 for(core::map<u16, RemoteClient*>::Iterator
3424 i = m_clients.getIterator();
3425 i.atEnd() == false; i++)
3427 // Get client and check that it is valid
3428 RemoteClient *client = i.getNode()->getValue();
3429 assert(client->peer_id == i.getNode()->getKey());
3430 if(client->serialization_version == SER_FMT_VER_INVALID)
3433 // Don't send if it's the same one
3434 if(client->peer_id == ignore_id)
3440 Player *player = m_env.getPlayer(client->peer_id);
3443 // If player is far away, only set modified blocks not sent
3444 v3f player_pos = player->getPosition();
3445 if(player_pos.getDistanceFrom(p_f) > maxd)
3447 far_players->push_back(client->peer_id);
3454 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3455 SharedBuffer<u8> reply(replysize);
3456 writeU16(&reply[0], TOCLIENT_ADDNODE);
3457 writeS16(&reply[2], p.X);
3458 writeS16(&reply[4], p.Y);
3459 writeS16(&reply[6], p.Z);
3460 n.serialize(&reply[8], client->serialization_version);
3463 m_con.Send(client->peer_id, 0, reply, true);
3467 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3469 DSTACK(__FUNCTION_NAME);
3471 v3s16 p = block->getPos();
3475 bool completely_air = true;
3476 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3477 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3478 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3480 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3482 completely_air = false;
3483 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3488 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3490 dstream<<"[completely air] ";
3495 Create a packet with the block in the right format
3498 std::ostringstream os(std::ios_base::binary);
3499 block->serialize(os, ver);
3500 std::string s = os.str();
3501 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3503 u32 replysize = 8 + blockdata.getSize();
3504 SharedBuffer<u8> reply(replysize);
3505 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3506 writeS16(&reply[2], p.X);
3507 writeS16(&reply[4], p.Y);
3508 writeS16(&reply[6], p.Z);
3509 memcpy(&reply[8], *blockdata, blockdata.getSize());
3511 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3512 <<": \tpacket size: "<<replysize<<std::endl;*/
3517 m_con.Send(peer_id, 1, reply, true);
3520 void Server::SendBlocks(float dtime)
3522 DSTACK(__FUNCTION_NAME);
3524 JMutexAutoLock envlock(m_env_mutex);
3525 JMutexAutoLock conlock(m_con_mutex);
3527 //TimeTaker timer("Server::SendBlocks");
3529 core::array<PrioritySortedBlockTransfer> queue;
3531 s32 total_sending = 0;
3533 for(core::map<u16, RemoteClient*>::Iterator
3534 i = m_clients.getIterator();
3535 i.atEnd() == false; i++)
3537 RemoteClient *client = i.getNode()->getValue();
3538 assert(client->peer_id == i.getNode()->getKey());
3540 total_sending += client->SendingCount();
3542 if(client->serialization_version == SER_FMT_VER_INVALID)
3545 client->GetNextBlocks(this, dtime, queue);
3549 // Lowest priority number comes first.
3550 // Lowest is most important.
3553 for(u32 i=0; i<queue.size(); i++)
3555 //TODO: Calculate limit dynamically
3556 if(total_sending >= g_settings.getS32
3557 ("max_simultaneous_block_sends_server_total"))
3560 PrioritySortedBlockTransfer q = queue[i];
3562 MapBlock *block = NULL;
3565 block = m_env.getMap().getBlockNoCreate(q.pos);
3567 catch(InvalidPositionException &e)
3572 RemoteClient *client = getClient(q.peer_id);
3574 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3576 client->SentBlock(q.pos);
3586 void Server::UpdateCrafting(u16 peer_id)
3588 DSTACK(__FUNCTION_NAME);
3590 Player* player = m_env.getPlayer(peer_id);
3594 Calculate crafting stuff
3596 if(g_settings.getBool("creative_mode") == false)
3598 InventoryList *clist = player->inventory.getList("craft");
3599 InventoryList *rlist = player->inventory.getList("craftresult");
3601 if(rlist->getUsedSlots() == 0)
3602 player->craftresult_is_preview = true;
3604 if(rlist && player->craftresult_is_preview)
3606 rlist->clearItems();
3608 if(clist && rlist && player->craftresult_is_preview)
3610 InventoryItem *items[9];
3611 for(u16 i=0; i<9; i++)
3613 items[i] = clist->getItem(i);
3622 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3623 if(checkItemCombination(items, specs))
3625 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3634 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3635 if(checkItemCombination(items, specs))
3637 rlist->addItem(new CraftItem("Stick", 4));
3646 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3647 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3648 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3649 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3650 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3651 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3652 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3653 if(checkItemCombination(items, specs))
3655 //rlist->addItem(new MapBlockObjectItem("Sign"));
3656 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3665 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3666 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3667 if(checkItemCombination(items, specs))
3669 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3678 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3679 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3680 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3681 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3682 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3683 if(checkItemCombination(items, specs))
3685 rlist->addItem(new ToolItem("WPick", 0));
3694 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3695 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3696 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3697 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3698 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3699 if(checkItemCombination(items, specs))
3701 rlist->addItem(new ToolItem("STPick", 0));
3710 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3711 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3712 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3713 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3714 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3715 if(checkItemCombination(items, specs))
3717 rlist->addItem(new ToolItem("SteelPick", 0));
3726 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3727 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3728 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3729 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3730 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3731 if(checkItemCombination(items, specs))
3733 rlist->addItem(new ToolItem("MesePick", 0));
3742 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3743 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3744 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3745 if(checkItemCombination(items, specs))
3747 rlist->addItem(new ToolItem("WShovel", 0));
3756 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3757 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3758 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3759 if(checkItemCombination(items, specs))
3761 rlist->addItem(new ToolItem("STShovel", 0));
3770 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3771 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3772 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3773 if(checkItemCombination(items, specs))
3775 rlist->addItem(new ToolItem("SteelShovel", 0));
3784 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3785 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3786 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3787 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3788 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3789 if(checkItemCombination(items, specs))
3791 rlist->addItem(new ToolItem("WAxe", 0));
3800 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3801 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3802 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3803 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3804 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3805 if(checkItemCombination(items, specs))
3807 rlist->addItem(new ToolItem("STAxe", 0));
3816 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3817 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3818 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3819 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3820 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3821 if(checkItemCombination(items, specs))
3823 rlist->addItem(new ToolItem("SteelAxe", 0));
3832 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3833 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3834 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3835 if(checkItemCombination(items, specs))
3837 rlist->addItem(new ToolItem("WSword", 0));
3846 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3847 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3848 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3849 if(checkItemCombination(items, specs))
3851 rlist->addItem(new ToolItem("STSword", 0));
3860 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3861 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3862 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3863 if(checkItemCombination(items, specs))
3865 rlist->addItem(new ToolItem("SteelSword", 0));
3874 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3875 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3876 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3877 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3878 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3879 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3880 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882 if(checkItemCombination(items, specs))
3884 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3893 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3894 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3895 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3896 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3897 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3898 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3899 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3900 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3901 if(checkItemCombination(items, specs))
3903 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3912 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3913 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3914 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3915 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3916 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3917 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3918 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3919 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3920 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3921 if(checkItemCombination(items, specs))
3923 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3929 } // if creative_mode == false
3932 RemoteClient* Server::getClient(u16 peer_id)
3934 DSTACK(__FUNCTION_NAME);
3935 //JMutexAutoLock lock(m_con_mutex);
3936 core::map<u16, RemoteClient*>::Node *n;
3937 n = m_clients.find(peer_id);
3938 // A client should exist for all peers
3940 return n->getValue();
3943 std::wstring Server::getStatusString()
3945 std::wostringstream os(std::ios_base::binary);
3948 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3950 os<<L", uptime="<<m_uptime.get();
3951 // Information about clients
3953 for(core::map<u16, RemoteClient*>::Iterator
3954 i = m_clients.getIterator();
3955 i.atEnd() == false; i++)
3957 // Get client and check that it is valid
3958 RemoteClient *client = i.getNode()->getValue();
3959 assert(client->peer_id == i.getNode()->getKey());
3960 if(client->serialization_version == SER_FMT_VER_INVALID)
3963 Player *player = m_env.getPlayer(client->peer_id);
3964 // Get name of player
3965 std::wstring name = L"unknown";
3967 name = narrow_to_wide(player->getName());
3968 // Add name to information string
3972 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3973 os<<" WARNING: Map saving is disabled."<<std::endl;
3978 void setCreativeInventory(Player *player)
3980 player->resetInventory();
3982 // Give some good tools
3984 InventoryItem *item = new ToolItem("MesePick", 0);
3985 void* r = player->inventory.addItem("main", item);
3989 InventoryItem *item = new ToolItem("SteelPick", 0);
3990 void* r = player->inventory.addItem("main", item);
3994 InventoryItem *item = new ToolItem("SteelAxe", 0);
3995 void* r = player->inventory.addItem("main", item);
3999 InventoryItem *item = new ToolItem("SteelShovel", 0);
4000 void* r = player->inventory.addItem("main", item);
4008 // CONTENT_IGNORE-terminated list
4009 u8 material_items[] = {
4019 CONTENT_WATERSOURCE,
4027 u8 *mip = material_items;
4028 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4030 if(*mip == CONTENT_IGNORE)
4033 InventoryItem *item = new MaterialItem(*mip, 1);
4034 player->inventory.addItem("main", item);
4040 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4043 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4044 player->inventory.addItem("main", item);
4047 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4049 // Skip some materials
4050 if(i == CONTENT_WATER || i == CONTENT_TORCH
4051 || i == CONTENT_COALSTONE)
4054 InventoryItem *item = new MaterialItem(i, 1);
4055 player->inventory.addItem("main", item);
4061 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4062 void* r = player->inventory.addItem("main", item);
4067 v3f findSpawnPos(ServerMap &map)
4069 //return v3f(50,50,50)*BS;
4072 s16 groundheight = 0;
4074 // Try to find a good place a few times
4075 for(s32 i=0; i<1000; i++)
4078 // We're going to try to throw the player to this position
4079 nodepos = v2s16(-range + (myrand()%(range*2)),
4080 -range + (myrand()%(range*2)));
4081 v2s16 sectorpos = getNodeSectorPos(nodepos);
4082 // Get sector (NOTE: Don't get because it's slow)
4083 //m_env.getMap().emergeSector(sectorpos);
4084 // Get ground height at point (fallbacks to heightmap function)
4085 groundheight = map.findGroundLevel(nodepos);
4086 // Don't go underwater
4087 if(groundheight < WATER_LEVEL)
4089 //dstream<<"-> Underwater"<<std::endl;
4092 // Don't go to high places
4093 if(groundheight > WATER_LEVEL + 4)
4095 //dstream<<"-> Underwater"<<std::endl;
4099 // Found a good place
4100 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4104 // If no suitable place was not found, go above water at least.
4105 if(groundheight < WATER_LEVEL)
4106 groundheight = WATER_LEVEL;
4108 return intToFloat(v3s16(
4115 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4118 Try to get an existing player
4120 Player *player = m_env.getPlayer(name);
4123 // If player is already connected, cancel
4124 if(player->peer_id != 0)
4126 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4131 player->peer_id = peer_id;
4133 // Reset inventory to creative if in creative mode
4134 if(g_settings.getBool("creative_mode"))
4136 setCreativeInventory(player);
4143 If player with the wanted peer_id already exists, cancel.
4145 if(m_env.getPlayer(peer_id) != NULL)
4147 dstream<<"emergePlayer(): Player with wrong name but same"
4148 " peer_id already exists"<<std::endl;
4156 player = new ServerRemotePlayer();
4157 //player->peer_id = c.peer_id;
4158 //player->peer_id = PEER_ID_INEXISTENT;
4159 player->peer_id = peer_id;
4160 player->updateName(name);
4161 player->updatePassword(password);
4167 dstream<<"Server: Finding spawn place for player \""
4168 <<player->getName()<<"\""<<std::endl;
4170 v3f pos = findSpawnPos(m_env.getServerMap());
4172 player->setPosition(pos);
4175 Add player to environment
4178 m_env.addPlayer(player);
4181 Add stuff to inventory
4184 if(g_settings.getBool("creative_mode"))
4186 setCreativeInventory(player);
4188 else if(g_settings.getBool("give_initial_stuff"))
4191 InventoryItem *item = new ToolItem("SteelPick", 0);
4192 void* r = player->inventory.addItem("main", item);
4196 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4197 void* r = player->inventory.addItem("main", item);
4201 InventoryItem *item = new ToolItem("SteelAxe", 0);
4202 void* r = player->inventory.addItem("main", item);
4206 InventoryItem *item = new ToolItem("SteelShovel", 0);
4207 void* r = player->inventory.addItem("main", item);
4211 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4212 void* r = player->inventory.addItem("main", item);
4216 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4217 void* r = player->inventory.addItem("main", item);
4221 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4222 void* r = player->inventory.addItem("main", item);
4226 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4227 void* r = player->inventory.addItem("main", item);
4231 InventoryItem *item = new CraftItem("Stick", 4);
4232 void* r = player->inventory.addItem("main", item);
4236 InventoryItem *item = new ToolItem("WPick", 32000);
4237 void* r = player->inventory.addItem("main", item);
4241 InventoryItem *item = new ToolItem("STPick", 32000);
4242 void* r = player->inventory.addItem("main", item);
4246 for(u16 i=0; i<4; i++)
4248 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4249 bool r = player->inventory.addItem("main", item);
4252 /*// Give some other stuff
4254 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4255 bool r = player->inventory.addItem("main", item);
4262 } // create new player
4265 void Server::handlePeerChange(PeerChange &c)
4267 JMutexAutoLock envlock(m_env_mutex);
4268 JMutexAutoLock conlock(m_con_mutex);
4270 if(c.type == PEER_ADDED)
4277 core::map<u16, RemoteClient*>::Node *n;
4278 n = m_clients.find(c.peer_id);
4279 // The client shouldn't already exist
4283 RemoteClient *client = new RemoteClient();
4284 client->peer_id = c.peer_id;
4285 m_clients.insert(client->peer_id, client);
4288 else if(c.type == PEER_REMOVED)
4295 core::map<u16, RemoteClient*>::Node *n;
4296 n = m_clients.find(c.peer_id);
4297 // The client should exist
4301 Mark objects to be not known by the client
4303 RemoteClient *client = n->getValue();
4305 for(core::map<u16, bool>::Iterator
4306 i = client->m_known_objects.getIterator();
4307 i.atEnd()==false; i++)
4310 u16 id = i.getNode()->getKey();
4311 ServerActiveObject* obj = m_env.getActiveObject(id);
4313 if(obj && obj->m_known_by_count > 0)
4314 obj->m_known_by_count--;
4317 // Collect information about leaving in chat
4318 std::wstring message;
4320 std::wstring name = L"unknown";
4321 Player *player = m_env.getPlayer(c.peer_id);
4323 name = narrow_to_wide(player->getName());
4327 message += L" left game";
4329 message += L" (timed out)";
4334 m_env.removePlayer(c.peer_id);
4337 // Set player client disconnected
4339 Player *player = m_env.getPlayer(c.peer_id);
4341 player->peer_id = 0;
4345 delete m_clients[c.peer_id];
4346 m_clients.remove(c.peer_id);
4348 // Send player info to all remaining clients
4351 // Send leave chat message to all remaining clients
4352 BroadcastChatMessage(message);
4361 void Server::handlePeerChanges()
4363 while(m_peer_change_queue.size() > 0)
4365 PeerChange c = m_peer_change_queue.pop_front();
4367 dout_server<<"Server: Handling peer change: "
4368 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4371 handlePeerChange(c);
4375 void dedicated_server_loop(Server &server, bool &kill)
4377 DSTACK(__FUNCTION_NAME);
4379 std::cout<<DTIME<<std::endl;
4380 std::cout<<"========================"<<std::endl;
4381 std::cout<<"Running dedicated server"<<std::endl;
4382 std::cout<<"========================"<<std::endl;
4383 std::cout<<std::endl;
4387 // This is kind of a hack but can be done like this
4388 // because server.step() is very light
4392 if(server.getShutdownRequested() || kill)
4394 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4398 static int counter = 0;
4404 core::list<PlayerInfo> list = server.getPlayerInfo();
4405 core::list<PlayerInfo>::Iterator i;
4406 static u32 sum_old = 0;
4407 u32 sum = PIChecksum(list);
4410 std::cout<<DTIME<<"Player info:"<<std::endl;
4411 for(i=list.begin(); i!=list.end(); i++)
4413 i->PrintLine(&std::cout);