3 Copyright (C) 2010 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 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
34 void * ServerThread::Thread()
38 DSTACK(__FUNCTION_NAME);
40 BEGIN_DEBUG_EXCEPTION_HANDLER
45 //TimeTaker timer("AsyncRunStep() + Receive()");
48 //TimeTaker timer("AsyncRunStep()");
49 m_server->AsyncRunStep();
52 //dout_server<<"Running m_server->Receive()"<<std::endl;
55 catch(con::NoIncomingDataException &e)
58 catch(con::PeerNotFoundException &e)
60 dout_server<<"Server: PeerNotFoundException"<<std::endl;
64 END_DEBUG_EXCEPTION_HANDLER
69 void * EmergeThread::Thread()
73 DSTACK(__FUNCTION_NAME);
77 BEGIN_DEBUG_EXCEPTION_HANDLER
80 Get block info from queue, emerge them and send them
83 After queue is empty, exit.
87 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91 SharedPtr<QueuedBlockEmerge> q(qptr);
97 Do not generate over-limit
99 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
100 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
101 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
102 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
107 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
109 //TimeTaker timer("block emerge");
112 Try to emerge it from somewhere.
114 If it is only wanted as optional, only loading from disk
119 Check if any peer wants it as non-optional. In that case it
122 Also decrement the emerge queue count in clients.
125 bool optional = true;
128 core::map<u16, u8>::Iterator i;
129 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
131 //u16 peer_id = i.getNode()->getKey();
134 u8 flags = i.getNode()->getValue();
135 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
141 /*dstream<<"EmergeThread: p="
142 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
143 <<"optional="<<optional<<std::endl;*/
145 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
147 core::map<v3s16, MapBlock*> changed_blocks;
148 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
150 MapBlock *block = NULL;
151 bool got_block = true;
152 core::map<v3s16, MapBlock*> modified_blocks;
154 bool only_from_disk = false;
157 only_from_disk = true;
159 v2s16 chunkpos = map.sector_to_chunk(p2d);
161 bool generate_chunk = false;
162 if(only_from_disk == false)
164 JMutexAutoLock envlock(m_server->m_env_mutex);
165 if(map.chunkNonVolatile(chunkpos) == false)
166 generate_chunk = true;
173 JMutexAutoLock envlock(m_server->m_env_mutex);
174 map.initChunkMake(data, chunkpos);
180 JMutexAutoLock envlock(m_server->m_env_mutex);
181 map.finishChunkMake(data, changed_blocks);
186 Fetch block from map or generate a single block
189 JMutexAutoLock envlock(m_server->m_env_mutex);
191 // Load sector if it isn't loaded
192 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
193 map.loadSectorFull(p2d);
195 block = map.getBlockNoCreateNoEx(p);
196 if(!block || block->isDummy())
204 // Get, load or create sector
205 ServerMapSector *sector =
206 (ServerMapSector*)map.createSector(p2d);
208 block = map.generateBlock(p, block, sector, changed_blocks,
209 lighting_invalidated_blocks);
216 if(block->getLightingExpired()){
217 lighting_invalidated_blocks[block->getPos()] = block;
221 // TODO: Some additional checking and lighting updating,
226 JMutexAutoLock envlock(m_server->m_env_mutex);
231 Collect a list of blocks that have been modified in
232 addition to the fetched one.
235 if(lighting_invalidated_blocks.size() > 0)
237 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
238 <<" blocks"<<std::endl;*/
240 // 50-100ms for single block generation
241 //TimeTaker timer("** EmergeThread updateLighting");
243 // Update lighting without locking the environment mutex,
244 // add modified blocks to changed blocks
245 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
248 // Add all from changed_blocks to modified_blocks
249 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
250 i.atEnd() == false; i++)
252 MapBlock *block = i.getNode()->getValue();
253 modified_blocks.insert(block->getPos(), block);
256 // If we got no block, there should be no invalidated blocks
259 assert(lighting_invalidated_blocks.size() == 0);
265 Set sent status of modified blocks on clients
268 // NOTE: Server's clients are also behind the connection mutex
269 JMutexAutoLock lock(m_server->m_con_mutex);
272 Add the originally fetched block to the modified list
276 modified_blocks.insert(p, block);
280 Set the modified blocks unsent for all the clients
283 for(core::map<u16, RemoteClient*>::Iterator
284 i = m_server->m_clients.getIterator();
285 i.atEnd() == false; i++)
287 RemoteClient *client = i.getNode()->getValue();
289 if(modified_blocks.size() > 0)
291 // Remove block from sent history
292 client->SetBlocksNotSent(modified_blocks);
298 END_DEBUG_EXCEPTION_HANDLER
303 void RemoteClient::GetNextBlocks(Server *server, float dtime,
304 core::array<PrioritySortedBlockTransfer> &dest)
306 DSTACK(__FUNCTION_NAME);
309 m_nearest_unsent_reset_timer += dtime;
311 // Won't send anything if already sending
312 if(m_blocks_sending.size() >= g_settings.getU16
313 ("max_simultaneous_block_sends_per_client"))
315 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
319 Player *player = server->m_env.getPlayer(peer_id);
321 assert(player != NULL);
323 v3f playerpos = player->getPosition();
324 v3f playerspeed = player->getSpeed();
326 v3s16 center_nodepos = floatToInt(playerpos, BS);
328 v3s16 center = getNodeBlockPos(center_nodepos);
330 // Camera position and direction
332 playerpos + v3f(0, BS+BS/2, 0);
333 v3f camera_dir = v3f(0,0,1);
334 camera_dir.rotateYZBy(player->getPitch());
335 camera_dir.rotateXZBy(player->getYaw());
338 Get the starting value of the block finder radius.
340 s16 last_nearest_unsent_d;
343 if(m_last_center != center)
345 m_nearest_unsent_d = 0;
346 m_last_center = center;
349 /*dstream<<"m_nearest_unsent_reset_timer="
350 <<m_nearest_unsent_reset_timer<<std::endl;*/
351 if(m_nearest_unsent_reset_timer > 5.0)
353 m_nearest_unsent_reset_timer = 0;
354 m_nearest_unsent_d = 0;
355 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
358 last_nearest_unsent_d = m_nearest_unsent_d;
360 d_start = m_nearest_unsent_d;
362 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
363 ("max_simultaneous_block_sends_per_client");
364 u16 maximum_simultaneous_block_sends =
365 maximum_simultaneous_block_sends_setting;
368 Check the time from last addNode/removeNode.
370 Decrease send rate if player is building stuff.
372 m_time_from_building += dtime;
373 if(m_time_from_building < g_settings.getFloat(
374 "full_block_send_enable_min_time_from_building"))
376 maximum_simultaneous_block_sends
377 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
380 u32 num_blocks_selected = m_blocks_sending.size();
383 next time d will be continued from the d from which the nearest
384 unsent block was found this time.
386 This is because not necessarily any of the blocks found this
387 time are actually sent.
389 s32 new_nearest_unsent_d = -1;
391 s16 d_max = g_settings.getS16("max_block_send_distance");
392 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
394 //dstream<<"Starting from "<<d_start<<std::endl;
396 for(s16 d = d_start; d <= d_max; d++)
398 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
401 If m_nearest_unsent_d was changed by the EmergeThread
402 (it can change it to 0 through SetBlockNotSent),
404 Else update m_nearest_unsent_d
406 if(m_nearest_unsent_d != last_nearest_unsent_d)
408 d = m_nearest_unsent_d;
409 last_nearest_unsent_d = m_nearest_unsent_d;
413 Get the border/face dot coordinates of a "d-radiused"
416 core::list<v3s16> list;
417 getFacePositions(list, d);
419 core::list<v3s16>::Iterator li;
420 for(li=list.begin(); li!=list.end(); li++)
422 v3s16 p = *li + center;
426 - Don't allow too many simultaneous transfers
427 - EXCEPT when the blocks are very close
429 Also, don't send blocks that are already flying.
432 u16 maximum_simultaneous_block_sends_now =
433 maximum_simultaneous_block_sends;
435 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
437 maximum_simultaneous_block_sends_now =
438 maximum_simultaneous_block_sends_setting;
441 // Limit is dynamically lowered when building
442 if(num_blocks_selected
443 >= maximum_simultaneous_block_sends_now)
445 /*dstream<<"Not sending more blocks. Queue full. "
446 <<m_blocks_sending.size()
451 if(m_blocks_sending.find(p) != NULL)
457 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
458 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
459 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
460 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
461 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
462 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
465 // If this is true, inexistent block will be made from scratch
466 bool generate = d <= d_max_gen;
469 /*// Limit the generating area vertically to 2/3
470 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
473 // Limit the send area vertically to 2/3
474 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
480 If block is far away, don't generate it unless it is
483 NOTE: We can't know the ground level this way with the
489 MapSector *sector = NULL;
492 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
494 catch(InvalidPositionException &e)
500 // Get center ground height in nodes
501 f32 gh = sector->getGroundHeight(
502 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
503 // Block center y in nodes
504 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
505 // If differs a lot, don't generate
506 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
513 Don't generate or send if not in sight
516 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
522 Don't send already sent blocks
525 if(m_blocks_sent.find(p) != NULL)
530 Check if map has this block
532 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
534 bool surely_not_found_on_disk = false;
535 bool block_is_invalid = false;
540 surely_not_found_on_disk = true;
543 if(block->isValid() == false)
545 block_is_invalid = true;
548 /*if(block->isFullyGenerated() == false)
550 block_is_invalid = true;
554 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
555 v2s16 chunkpos = map->sector_to_chunk(p2d);
556 if(map->chunkNonVolatile(chunkpos) == false)
557 block_is_invalid = true;
561 If block has been marked to not exist on disk (dummy)
562 and generating new ones is not wanted, skip block.
564 if(generate == false && surely_not_found_on_disk == true)
571 Record the lowest d from which a a block has been
572 found being not sent and possibly to exist
574 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
576 new_nearest_unsent_d = d;
580 Add inexistent block to emerge queue.
582 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
584 //TODO: Get value from somewhere
585 // Allow only one block in emerge queue
586 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
587 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
589 //dstream<<"Adding block to emerge queue"<<std::endl;
591 // Add it to the emerge queue and trigger the thread
594 if(generate == false)
595 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
597 server->m_emerge_queue.addBlock(peer_id, p, flags);
598 server->m_emergethread.trigger();
606 Add block to send queue
609 PrioritySortedBlockTransfer q((float)d, p, peer_id);
613 num_blocks_selected += 1;
618 if(new_nearest_unsent_d != -1)
620 m_nearest_unsent_d = new_nearest_unsent_d;
624 void RemoteClient::SendObjectData(
627 core::map<v3s16, bool> &stepped_blocks
630 DSTACK(__FUNCTION_NAME);
632 // Can't send anything without knowing version
633 if(serialization_version == SER_FMT_VER_INVALID)
635 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
641 Send a TOCLIENT_OBJECTDATA packet.
645 u16 number of player positions
656 std::ostringstream os(std::ios_base::binary);
660 writeU16(buf, TOCLIENT_OBJECTDATA);
661 os.write((char*)buf, 2);
664 Get and write player data
667 // Get connected players
668 core::list<Player*> players = server->m_env.getPlayers(true);
670 // Write player count
671 u16 playercount = players.size();
672 writeU16(buf, playercount);
673 os.write((char*)buf, 2);
675 core::list<Player*>::Iterator i;
676 for(i = players.begin();
677 i != players.end(); i++)
681 v3f pf = player->getPosition();
682 v3f sf = player->getSpeed();
684 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
685 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
686 s32 pitch_i (player->getPitch() * 100);
687 s32 yaw_i (player->getYaw() * 100);
689 writeU16(buf, player->peer_id);
690 os.write((char*)buf, 2);
691 writeV3S32(buf, position_i);
692 os.write((char*)buf, 12);
693 writeV3S32(buf, speed_i);
694 os.write((char*)buf, 12);
695 writeS32(buf, pitch_i);
696 os.write((char*)buf, 4);
697 writeS32(buf, yaw_i);
698 os.write((char*)buf, 4);
702 Get and write object data
708 For making players to be able to build to their nearby
709 environment (building is not possible on blocks that are not
712 - Add blocks to emerge queue if they are not found
714 SUGGESTION: These could be ignored from the backside of the player
717 Player *player = server->m_env.getPlayer(peer_id);
721 v3f playerpos = player->getPosition();
722 v3f playerspeed = player->getSpeed();
724 v3s16 center_nodepos = floatToInt(playerpos, BS);
725 v3s16 center = getNodeBlockPos(center_nodepos);
727 s16 d_max = g_settings.getS16("active_object_range");
729 // Number of blocks whose objects were written to bos
732 std::ostringstream bos(std::ios_base::binary);
734 for(s16 d = 0; d <= d_max; d++)
736 core::list<v3s16> list;
737 getFacePositions(list, d);
739 core::list<v3s16>::Iterator li;
740 for(li=list.begin(); li!=list.end(); li++)
742 v3s16 p = *li + center;
745 Ignore blocks that haven't been sent to the client
748 if(m_blocks_sent.find(p) == NULL)
752 // Try stepping block and add it to a send queue
757 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
760 Step block if not in stepped_blocks and add to stepped_blocks.
762 if(stepped_blocks.find(p) == NULL)
764 block->stepObjects(dtime, true, server->getDayNightRatio());
765 stepped_blocks.insert(p, true);
766 block->setChangedFlag();
769 // Skip block if there are no objects
770 if(block->getObjectCount() == 0)
779 bos.write((char*)buf, 6);
782 block->serializeObjects(bos, serialization_version);
787 Stop collecting objects if data is already too big
789 // Sum of player and object data sizes
790 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
791 // break out if data too big
792 if(sum > MAX_OBJECTDATA_SIZE)
794 goto skip_subsequent;
798 catch(InvalidPositionException &e)
801 // Add it to the emerge queue and trigger the thread.
802 // Fetch the block only if it is on disk.
804 // Grab and increment counter
805 /*SharedPtr<JMutexAutoLock> lock
806 (m_num_blocks_in_emerge_queue.getLock());
807 m_num_blocks_in_emerge_queue.m_value++;*/
809 // Add to queue as an anonymous fetch from disk
810 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
811 server->m_emerge_queue.addBlock(0, p, flags);
812 server->m_emergethread.trigger();
820 writeU16(buf, blockcount);
821 os.write((char*)buf, 2);
823 // Write block objects
830 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
833 std::string s = os.str();
834 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
835 // Send as unreliable
836 server->m_con.Send(peer_id, 0, data, false);
839 void RemoteClient::GotBlock(v3s16 p)
841 if(m_blocks_sending.find(p) != NULL)
842 m_blocks_sending.remove(p);
845 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
846 " m_blocks_sending"<<std::endl;*/
847 m_excess_gotblocks++;
849 m_blocks_sent.insert(p, true);
852 void RemoteClient::SentBlock(v3s16 p)
854 if(m_blocks_sending.find(p) == NULL)
855 m_blocks_sending.insert(p, 0.0);
857 dstream<<"RemoteClient::SentBlock(): Sent block"
858 " already in m_blocks_sending"<<std::endl;
861 void RemoteClient::SetBlockNotSent(v3s16 p)
863 m_nearest_unsent_d = 0;
865 if(m_blocks_sending.find(p) != NULL)
866 m_blocks_sending.remove(p);
867 if(m_blocks_sent.find(p) != NULL)
868 m_blocks_sent.remove(p);
871 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
873 m_nearest_unsent_d = 0;
875 for(core::map<v3s16, MapBlock*>::Iterator
876 i = blocks.getIterator();
877 i.atEnd()==false; i++)
879 v3s16 p = i.getNode()->getKey();
881 if(m_blocks_sending.find(p) != NULL)
882 m_blocks_sending.remove(p);
883 if(m_blocks_sent.find(p) != NULL)
884 m_blocks_sent.remove(p);
892 PlayerInfo::PlayerInfo()
898 void PlayerInfo::PrintLine(std::ostream *s)
901 (*s)<<"\""<<name<<"\" ("
902 <<(position.X/10)<<","<<(position.Y/10)
903 <<","<<(position.Z/10)<<") ";
905 (*s)<<" avg_rtt="<<avg_rtt;
909 u32 PIChecksum(core::list<PlayerInfo> &l)
911 core::list<PlayerInfo>::Iterator i;
914 for(i=l.begin(); i!=l.end(); i++)
916 checksum += a * (i->id+1);
917 checksum ^= 0x435aafcd;
928 std::string mapsavedir
930 m_env(new ServerMap(mapsavedir), this),
931 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
933 m_emergethread(this),
936 m_time_of_day_send_timer(0),
938 m_mapsavedir(mapsavedir),
939 m_shutdown_requested(false),
940 m_ignore_map_edit_events(false),
941 m_ignore_map_edit_events_peer_id(0)
943 m_liquid_transform_timer = 0.0;
944 m_print_info_timer = 0.0;
945 m_objectdata_timer = 0.0;
946 m_emergethread_trigger_timer = 0.0;
947 m_savemap_timer = 0.0;
951 m_step_dtime_mutex.Init();
954 m_env.getMap().addEventReceiver(this);
957 m_env.deSerializePlayers(m_mapsavedir);
962 dstream<<"Server::~Server()"<<std::endl;
965 Send shutdown message
968 JMutexAutoLock conlock(m_con_mutex);
970 std::wstring line = L"*** Server shutting down";
973 Send the message to clients
975 for(core::map<u16, RemoteClient*>::Iterator
976 i = m_clients.getIterator();
977 i.atEnd() == false; i++)
979 // Get client and check that it is valid
980 RemoteClient *client = i.getNode()->getValue();
981 assert(client->peer_id == i.getNode()->getKey());
982 if(client->serialization_version == SER_FMT_VER_INVALID)
986 SendChatMessage(client->peer_id, line);
988 catch(con::PeerNotFoundException &e)
996 dstream<<"Server: Saving players"<<std::endl;
997 m_env.serializePlayers(m_mapsavedir);
1008 JMutexAutoLock clientslock(m_con_mutex);
1010 for(core::map<u16, RemoteClient*>::Iterator
1011 i = m_clients.getIterator();
1012 i.atEnd() == false; i++)
1015 // NOTE: These are removed by env destructor
1017 u16 peer_id = i.getNode()->getKey();
1018 JMutexAutoLock envlock(m_env_mutex);
1019 m_env.removePlayer(peer_id);
1023 delete i.getNode()->getValue();
1028 void Server::start(unsigned short port)
1030 DSTACK(__FUNCTION_NAME);
1031 // Stop thread if already running
1034 // Initialize connection
1035 m_con.setTimeoutMs(30);
1039 m_thread.setRun(true);
1042 dout_server<<"Server: Started on port "<<port<<std::endl;
1047 DSTACK(__FUNCTION_NAME);
1049 // Stop threads (set run=false first so both start stopping)
1050 m_thread.setRun(false);
1051 m_emergethread.setRun(false);
1053 m_emergethread.stop();
1055 dout_server<<"Server: Threads stopped"<<std::endl;
1058 void Server::step(float dtime)
1060 DSTACK(__FUNCTION_NAME);
1065 JMutexAutoLock lock(m_step_dtime_mutex);
1066 m_step_dtime += dtime;
1070 void Server::AsyncRunStep()
1072 DSTACK(__FUNCTION_NAME);
1076 JMutexAutoLock lock1(m_step_dtime_mutex);
1077 dtime = m_step_dtime;
1080 // Send blocks to clients
1086 //dstream<<"Server steps "<<dtime<<std::endl;
1087 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1090 JMutexAutoLock lock1(m_step_dtime_mutex);
1091 m_step_dtime -= dtime;
1098 m_uptime.set(m_uptime.get() + dtime);
1102 Update m_time_of_day
1105 m_time_counter += dtime;
1106 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1107 u32 units = (u32)(m_time_counter*speed);
1108 m_time_counter -= (f32)units / speed;
1109 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1111 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1114 Send to clients at constant intervals
1117 m_time_of_day_send_timer -= dtime;
1118 if(m_time_of_day_send_timer < 0.0)
1120 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1122 //JMutexAutoLock envlock(m_env_mutex);
1123 JMutexAutoLock conlock(m_con_mutex);
1125 for(core::map<u16, RemoteClient*>::Iterator
1126 i = m_clients.getIterator();
1127 i.atEnd() == false; i++)
1129 RemoteClient *client = i.getNode()->getValue();
1130 //Player *player = m_env.getPlayer(client->peer_id);
1132 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1133 m_time_of_day.get());
1135 m_con.Send(client->peer_id, 0, data, true);
1141 // Process connection's timeouts
1142 JMutexAutoLock lock2(m_con_mutex);
1143 m_con.RunTimeouts(dtime);
1147 // This has to be called so that the client list gets synced
1148 // with the peer list of the connection
1149 handlePeerChanges();
1154 // This also runs Map's timers
1155 JMutexAutoLock lock(m_env_mutex);
1166 m_liquid_transform_timer += dtime;
1167 if(m_liquid_transform_timer >= 1.00)
1169 m_liquid_transform_timer -= 1.00;
1171 JMutexAutoLock lock(m_env_mutex);
1173 core::map<v3s16, MapBlock*> modified_blocks;
1174 m_env.getMap().transformLiquids(modified_blocks);
1179 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1180 ServerMap &map = ((ServerMap&)m_env.getMap());
1181 map.updateLighting(modified_blocks, lighting_modified_blocks);
1183 // Add blocks modified by lighting to modified_blocks
1184 for(core::map<v3s16, MapBlock*>::Iterator
1185 i = lighting_modified_blocks.getIterator();
1186 i.atEnd() == false; i++)
1188 MapBlock *block = i.getNode()->getValue();
1189 modified_blocks.insert(block->getPos(), block);
1193 Set the modified blocks unsent for all the clients
1196 JMutexAutoLock lock2(m_con_mutex);
1198 for(core::map<u16, RemoteClient*>::Iterator
1199 i = m_clients.getIterator();
1200 i.atEnd() == false; i++)
1202 RemoteClient *client = i.getNode()->getValue();
1204 if(modified_blocks.size() > 0)
1206 // Remove block from sent history
1207 client->SetBlocksNotSent(modified_blocks);
1212 // Periodically print some info
1214 float &counter = m_print_info_timer;
1220 JMutexAutoLock lock2(m_con_mutex);
1222 for(core::map<u16, RemoteClient*>::Iterator
1223 i = m_clients.getIterator();
1224 i.atEnd() == false; i++)
1226 //u16 peer_id = i.getNode()->getKey();
1227 RemoteClient *client = i.getNode()->getValue();
1228 Player *player = m_env.getPlayer(client->peer_id);
1231 std::cout<<player->getName()<<"\t";
1232 client->PrintInfo(std::cout);
1237 //if(g_settings.getBool("enable_experimental"))
1241 Check added and deleted active objects
1244 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1246 JMutexAutoLock envlock(m_env_mutex);
1247 JMutexAutoLock conlock(m_con_mutex);
1249 // Radius inside which objects are active
1252 for(core::map<u16, RemoteClient*>::Iterator
1253 i = m_clients.getIterator();
1254 i.atEnd() == false; i++)
1256 RemoteClient *client = i.getNode()->getValue();
1257 Player *player = m_env.getPlayer(client->peer_id);
1260 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1261 <<" has no associated player"<<std::endl;
1264 v3s16 pos = floatToInt(player->getPosition(), BS);
1266 core::map<u16, bool> removed_objects;
1267 core::map<u16, bool> added_objects;
1268 m_env.getRemovedActiveObjects(pos, radius,
1269 client->m_known_objects, removed_objects);
1270 m_env.getAddedActiveObjects(pos, radius,
1271 client->m_known_objects, added_objects);
1273 // Ignore if nothing happened
1274 if(removed_objects.size() == 0 && added_objects.size() == 0)
1276 //dstream<<"INFO: active objects: none changed"<<std::endl;
1280 std::string data_buffer;
1284 // Handle removed objects
1285 writeU16((u8*)buf, removed_objects.size());
1286 data_buffer.append(buf, 2);
1287 for(core::map<u16, bool>::Iterator
1288 i = removed_objects.getIterator();
1289 i.atEnd()==false; i++)
1292 u16 id = i.getNode()->getKey();
1293 ServerActiveObject* obj = m_env.getActiveObject(id);
1295 // Add to data buffer for sending
1296 writeU16((u8*)buf, i.getNode()->getKey());
1297 data_buffer.append(buf, 2);
1299 // Remove from known objects
1300 client->m_known_objects.remove(i.getNode()->getKey());
1302 if(obj && obj->m_known_by_count > 0)
1303 obj->m_known_by_count--;
1306 // Handle added objects
1307 writeU16((u8*)buf, added_objects.size());
1308 data_buffer.append(buf, 2);
1309 for(core::map<u16, bool>::Iterator
1310 i = added_objects.getIterator();
1311 i.atEnd()==false; i++)
1314 u16 id = i.getNode()->getKey();
1315 ServerActiveObject* obj = m_env.getActiveObject(id);
1318 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1320 dstream<<"WARNING: "<<__FUNCTION_NAME
1321 <<": NULL object"<<std::endl;
1323 type = obj->getType();
1325 // Add to data buffer for sending
1326 writeU16((u8*)buf, id);
1327 data_buffer.append(buf, 2);
1328 writeU8((u8*)buf, type);
1329 data_buffer.append(buf, 1);
1332 data_buffer.append(serializeLongString(
1333 obj->getClientInitializationData()));
1335 data_buffer.append(serializeLongString(""));
1337 // Add to known objects
1338 client->m_known_objects.insert(i.getNode()->getKey(), false);
1341 obj->m_known_by_count++;
1345 SharedBuffer<u8> reply(2 + data_buffer.size());
1346 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1347 memcpy((char*)&reply[2], data_buffer.c_str(),
1348 data_buffer.size());
1350 m_con.Send(client->peer_id, 0, reply, true);
1352 dstream<<"INFO: Server: Sent object remove/add: "
1353 <<removed_objects.size()<<" removed, "
1354 <<added_objects.size()<<" added, "
1355 <<"packet size is "<<reply.getSize()<<std::endl;
1360 Collect a list of all the objects known by the clients
1361 and report it back to the environment.
1364 core::map<u16, bool> all_known_objects;
1366 for(core::map<u16, RemoteClient*>::Iterator
1367 i = m_clients.getIterator();
1368 i.atEnd() == false; i++)
1370 RemoteClient *client = i.getNode()->getValue();
1371 // Go through all known objects of client
1372 for(core::map<u16, bool>::Iterator
1373 i = client->m_known_objects.getIterator();
1374 i.atEnd()==false; i++)
1376 u16 id = i.getNode()->getKey();
1377 all_known_objects[id] = true;
1381 m_env.setKnownActiveObjects(whatever);
1387 Send object messages
1390 JMutexAutoLock envlock(m_env_mutex);
1391 JMutexAutoLock conlock(m_con_mutex);
1394 // Value = data sent by object
1395 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1397 // Get active object messages from environment
1400 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1404 core::list<ActiveObjectMessage>* message_list = NULL;
1405 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1406 n = buffered_messages.find(aom.id);
1409 message_list = new core::list<ActiveObjectMessage>;
1410 buffered_messages.insert(aom.id, message_list);
1414 message_list = n->getValue();
1416 message_list->push_back(aom);
1419 // Route data to every client
1420 for(core::map<u16, RemoteClient*>::Iterator
1421 i = m_clients.getIterator();
1422 i.atEnd()==false; i++)
1424 RemoteClient *client = i.getNode()->getValue();
1425 std::string reliable_data;
1426 std::string unreliable_data;
1427 // Go through all objects in message buffer
1428 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1429 j = buffered_messages.getIterator();
1430 j.atEnd()==false; j++)
1432 // If object is not known by client, skip it
1433 u16 id = j.getNode()->getKey();
1434 if(client->m_known_objects.find(id) == NULL)
1436 // Get message list of object
1437 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1438 // Go through every message
1439 for(core::list<ActiveObjectMessage>::Iterator
1440 k = list->begin(); k != list->end(); k++)
1442 // Compose the full new data with header
1443 ActiveObjectMessage aom = *k;
1444 std::string new_data;
1447 writeU16((u8*)&buf[0], aom.id);
1448 new_data.append(buf, 2);
1450 new_data += serializeString(aom.datastring);
1451 // Add data to buffer
1453 reliable_data += new_data;
1455 unreliable_data += new_data;
1459 reliable_data and unreliable_data are now ready.
1462 if(reliable_data.size() > 0)
1464 SharedBuffer<u8> reply(2 + reliable_data.size());
1465 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1466 memcpy((char*)&reply[2], reliable_data.c_str(),
1467 reliable_data.size());
1469 m_con.Send(client->peer_id, 0, reply, true);
1471 if(unreliable_data.size() > 0)
1473 SharedBuffer<u8> reply(2 + unreliable_data.size());
1474 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1475 memcpy((char*)&reply[2], unreliable_data.c_str(),
1476 unreliable_data.size());
1477 // Send as unreliable
1478 m_con.Send(client->peer_id, 0, reply, false);
1481 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1483 dstream<<"INFO: Server: Size of object message data: "
1484 <<"reliable: "<<reliable_data.size()
1485 <<", unreliable: "<<unreliable_data.size()
1490 // Clear buffered_messages
1491 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1492 i = buffered_messages.getIterator();
1493 i.atEnd()==false; i++)
1495 delete i.getNode()->getValue();
1499 } // enable_experimental
1502 Send queued-for-sending map edit events.
1505 while(m_unsent_map_edit_queue.size() != 0)
1507 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1509 if(event->type == MEET_ADDNODE)
1511 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1512 sendAddNode(event->p, event->n, event->already_known_by_peer);
1514 else if(event->type == MEET_REMOVENODE)
1516 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1517 sendRemoveNode(event->p, event->already_known_by_peer);
1519 else if(event->type == MEET_OTHER)
1521 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1526 dstream<<"WARNING: Server: Unknown MapEditEvent "
1527 <<((u32)event->type)<<std::endl;
1535 Send object positions
1536 TODO: Get rid of MapBlockObjects
1539 float &counter = m_objectdata_timer;
1541 if(counter >= g_settings.getFloat("objectdata_interval"))
1543 JMutexAutoLock lock1(m_env_mutex);
1544 JMutexAutoLock lock2(m_con_mutex);
1545 SendObjectData(counter);
1555 //TimeTaker timer("Step node metadata");
1557 JMutexAutoLock envlock(m_env_mutex);
1558 JMutexAutoLock conlock(m_con_mutex);
1560 core::map<v3s16, MapBlock*> changed_blocks;
1561 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1563 for(core::map<v3s16, MapBlock*>::Iterator
1564 i = changed_blocks.getIterator();
1565 i.atEnd() == false; i++)
1567 MapBlock *block = i.getNode()->getValue();
1569 for(core::map<u16, RemoteClient*>::Iterator
1570 i = m_clients.getIterator();
1571 i.atEnd()==false; i++)
1573 RemoteClient *client = i.getNode()->getValue();
1574 client->SetBlockNotSent(block->getPos());
1580 Trigger emergethread (it somehow gets to a non-triggered but
1581 bysy state sometimes)
1584 float &counter = m_emergethread_trigger_timer;
1590 m_emergethread.trigger();
1596 float &counter = m_savemap_timer;
1598 if(counter >= g_settings.getFloat("server_map_save_interval"))
1602 JMutexAutoLock lock(m_env_mutex);
1604 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1606 // Save only changed parts
1607 m_env.getMap().save(true);
1609 // Delete unused sectors
1610 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1611 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1612 if(deleted_count > 0)
1614 dout_server<<"Server: Unloaded "<<deleted_count
1615 <<" sectors from memory"<<std::endl;
1619 m_env.serializePlayers(m_mapsavedir);
1625 void Server::Receive()
1627 DSTACK(__FUNCTION_NAME);
1628 u32 data_maxsize = 10000;
1629 Buffer<u8> data(data_maxsize);
1634 JMutexAutoLock conlock(m_con_mutex);
1635 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1638 // This has to be called so that the client list gets synced
1639 // with the peer list of the connection
1640 handlePeerChanges();
1642 ProcessData(*data, datasize, peer_id);
1644 catch(con::InvalidIncomingDataException &e)
1646 derr_server<<"Server::Receive(): "
1647 "InvalidIncomingDataException: what()="
1648 <<e.what()<<std::endl;
1650 catch(con::PeerNotFoundException &e)
1652 //NOTE: This is not needed anymore
1654 // The peer has been disconnected.
1655 // Find the associated player and remove it.
1657 /*JMutexAutoLock envlock(m_env_mutex);
1659 dout_server<<"ServerThread: peer_id="<<peer_id
1660 <<" has apparently closed connection. "
1661 <<"Removing player."<<std::endl;
1663 m_env.removePlayer(peer_id);*/
1667 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1669 DSTACK(__FUNCTION_NAME);
1670 // Environment is locked first.
1671 JMutexAutoLock envlock(m_env_mutex);
1672 JMutexAutoLock conlock(m_con_mutex);
1676 peer = m_con.GetPeer(peer_id);
1678 catch(con::PeerNotFoundException &e)
1680 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1681 <<peer_id<<" not found"<<std::endl;
1685 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1693 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1695 if(command == TOSERVER_INIT)
1697 // [0] u16 TOSERVER_INIT
1698 // [2] u8 SER_FMT_VER_HIGHEST
1699 // [3] u8[20] player_name
1704 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1705 <<peer->id<<std::endl;
1707 // First byte after command is maximum supported
1708 // serialization version
1709 u8 client_max = data[2];
1710 u8 our_max = SER_FMT_VER_HIGHEST;
1711 // Use the highest version supported by both
1712 u8 deployed = core::min_(client_max, our_max);
1713 // If it's lower than the lowest supported, give up.
1714 if(deployed < SER_FMT_VER_LOWEST)
1715 deployed = SER_FMT_VER_INVALID;
1717 //peer->serialization_version = deployed;
1718 getClient(peer->id)->pending_serialization_version = deployed;
1720 if(deployed == SER_FMT_VER_INVALID)
1722 derr_server<<DTIME<<"Server: Cannot negotiate "
1723 "serialization version with peer "
1724 <<peer_id<<std::endl;
1733 const u32 playername_size = 20;
1734 char playername[playername_size];
1735 for(u32 i=0; i<playername_size-1; i++)
1737 playername[i] = data[3+i];
1739 playername[playername_size-1] = 0;
1742 Player *player = emergePlayer(playername, "", peer_id);
1743 //Player *player = m_env.getPlayer(peer_id);
1746 // DEBUG: Test serialization
1747 std::ostringstream test_os;
1748 player->serialize(test_os);
1749 dstream<<"Player serialization test: \""<<test_os.str()
1751 std::istringstream test_is(test_os.str());
1752 player->deSerialize(test_is);
1755 // If failed, cancel
1758 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1759 <<": failed to emerge player"<<std::endl;
1764 // If a client is already connected to the player, cancel
1765 if(player->peer_id != 0)
1767 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1768 <<" tried to connect to "
1769 "an already connected player (peer_id="
1770 <<player->peer_id<<")"<<std::endl;
1773 // Set client of player
1774 player->peer_id = peer_id;
1777 // Check if player doesn't exist
1779 throw con::InvalidIncomingDataException
1780 ("Server::ProcessData(): INIT: Player doesn't exist");
1782 /*// update name if it was supplied
1783 if(datasize >= 20+3)
1786 player->updateName((const char*)&data[3]);
1790 Answer with a TOCLIENT_INIT
1793 SharedBuffer<u8> reply(2+1+6+8);
1794 writeU16(&reply[0], TOCLIENT_INIT);
1795 writeU8(&reply[2], deployed);
1796 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1797 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1798 writeU64(&reply[2+1+6], 0); // no seed
1801 m_con.Send(peer_id, 0, reply, true);
1805 Send complete position information
1807 SendMovePlayer(player);
1812 if(command == TOSERVER_INIT2)
1814 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1815 <<peer->id<<std::endl;
1818 getClient(peer->id)->serialization_version
1819 = getClient(peer->id)->pending_serialization_version;
1822 Send some initialization data
1825 // Send player info to all players
1828 // Send inventory to player
1829 UpdateCrafting(peer->id);
1830 SendInventory(peer->id);
1834 Player *player = m_env.getPlayer(peer_id);
1835 SendPlayerHP(player);
1840 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1841 m_time_of_day.get());
1842 m_con.Send(peer->id, 0, data, true);
1845 // Send information about server to player in chat
1846 SendChatMessage(peer_id, getStatusString());
1848 // Send information about joining in chat
1850 std::wstring name = L"unknown";
1851 Player *player = m_env.getPlayer(peer_id);
1853 name = narrow_to_wide(player->getName());
1855 std::wstring message;
1858 message += L" joined game";
1859 BroadcastChatMessage(message);
1865 if(peer_ser_ver == SER_FMT_VER_INVALID)
1867 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1868 " serialization format invalid or not initialized."
1869 " Skipping incoming command="<<command<<std::endl;
1873 Player *player = m_env.getPlayer(peer_id);
1876 derr_server<<"Server::ProcessData(): Cancelling: "
1877 "No player for peer_id="<<peer_id
1881 if(command == TOSERVER_PLAYERPOS)
1883 if(datasize < 2+12+12+4+4)
1887 v3s32 ps = readV3S32(&data[start+2]);
1888 v3s32 ss = readV3S32(&data[start+2+12]);
1889 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1890 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1891 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1892 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1893 pitch = wrapDegrees(pitch);
1894 yaw = wrapDegrees(yaw);
1895 player->setPosition(position);
1896 player->setSpeed(speed);
1897 player->setPitch(pitch);
1898 player->setYaw(yaw);
1900 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1901 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1902 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1904 else if(command == TOSERVER_GOTBLOCKS)
1917 u16 count = data[2];
1918 for(u16 i=0; i<count; i++)
1920 if((s16)datasize < 2+1+(i+1)*6)
1921 throw con::InvalidIncomingDataException
1922 ("GOTBLOCKS length is too short");
1923 v3s16 p = readV3S16(&data[2+1+i*6]);
1924 /*dstream<<"Server: GOTBLOCKS ("
1925 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1926 RemoteClient *client = getClient(peer_id);
1927 client->GotBlock(p);
1930 else if(command == TOSERVER_DELETEDBLOCKS)
1943 u16 count = data[2];
1944 for(u16 i=0; i<count; i++)
1946 if((s16)datasize < 2+1+(i+1)*6)
1947 throw con::InvalidIncomingDataException
1948 ("DELETEDBLOCKS length is too short");
1949 v3s16 p = readV3S16(&data[2+1+i*6]);
1950 /*dstream<<"Server: DELETEDBLOCKS ("
1951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1952 RemoteClient *client = getClient(peer_id);
1953 client->SetBlockNotSent(p);
1956 else if(command == TOSERVER_CLICK_OBJECT)
1963 [2] u8 button (0=left, 1=right)
1968 u8 button = readU8(&data[2]);
1970 p.X = readS16(&data[3]);
1971 p.Y = readS16(&data[5]);
1972 p.Z = readS16(&data[7]);
1973 s16 id = readS16(&data[9]);
1974 //u16 item_i = readU16(&data[11]);
1976 MapBlock *block = NULL;
1979 block = m_env.getMap().getBlockNoCreate(p);
1981 catch(InvalidPositionException &e)
1983 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1987 MapBlockObject *obj = block->getObject(id);
1991 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1995 //TODO: Check that object is reasonably close
2000 InventoryList *ilist = player->inventory.getList("main");
2001 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2004 // Skip if inventory has no free space
2005 if(ilist->getUsedSlots() == ilist->getSize())
2007 dout_server<<"Player inventory has no free space"<<std::endl;
2012 Create the inventory item
2014 InventoryItem *item = NULL;
2015 // If it is an item-object, take the item from it
2016 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2018 item = ((ItemObject*)obj)->createInventoryItem();
2020 // Else create an item of the object
2023 item = new MapBlockObjectItem
2024 (obj->getInventoryString());
2027 // Add to inventory and send inventory
2028 ilist->addItem(item);
2029 UpdateCrafting(player->peer_id);
2030 SendInventory(player->peer_id);
2033 // Remove from block
2034 block->removeObject(id);
2037 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2045 [2] u8 button (0=left, 1=right)
2049 u8 button = readU8(&data[2]);
2050 u16 id = readS16(&data[3]);
2051 u16 item_i = readU16(&data[11]);
2053 ServerActiveObject *obj = m_env.getActiveObject(id);
2057 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2062 //TODO: Check that object is reasonably close
2064 // Left click, pick object up (usually)
2067 InventoryList *ilist = player->inventory.getList("main");
2068 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2071 // Skip if inventory has no free space
2072 if(ilist->getUsedSlots() == ilist->getSize())
2074 dout_server<<"Player inventory has no free space"<<std::endl;
2078 // Skip if object has been removed
2083 Create the inventory item
2085 InventoryItem *item = obj->createPickedUpItem();
2089 // Add to inventory and send inventory
2090 ilist->addItem(item);
2091 UpdateCrafting(player->peer_id);
2092 SendInventory(player->peer_id);
2094 // Remove object from environment
2095 obj->m_removed = true;
2100 Item cannot be picked up. Punch it instead.
2103 ToolItem *titem = NULL;
2104 std::string toolname = "";
2106 InventoryList *mlist = player->inventory.getList("main");
2109 InventoryItem *item = mlist->getItem(item_i);
2110 if(item && (std::string)item->getName() == "ToolItem")
2112 titem = (ToolItem*)item;
2113 toolname = titem->getToolName();
2117 u16 wear = obj->punch(toolname);
2121 bool weared_out = titem->addWear(wear);
2123 mlist->deleteItem(item_i);
2124 SendInventory(player->peer_id);
2130 else if(command == TOSERVER_GROUND_ACTION)
2138 [3] v3s16 nodepos_undersurface
2139 [9] v3s16 nodepos_abovesurface
2144 2: stop digging (all parameters ignored)
2145 3: digging completed
2147 u8 action = readU8(&data[2]);
2149 p_under.X = readS16(&data[3]);
2150 p_under.Y = readS16(&data[5]);
2151 p_under.Z = readS16(&data[7]);
2153 p_over.X = readS16(&data[9]);
2154 p_over.Y = readS16(&data[11]);
2155 p_over.Z = readS16(&data[13]);
2156 u16 item_i = readU16(&data[15]);
2158 //TODO: Check that target is reasonably close
2166 NOTE: This can be used in the future to check if
2167 somebody is cheating, by checking the timing.
2174 else if(action == 2)
2177 RemoteClient *client = getClient(peer->id);
2178 JMutexAutoLock digmutex(client->m_dig_mutex);
2179 client->m_dig_tool_item = -1;
2184 3: Digging completed
2186 else if(action == 3)
2188 // Mandatory parameter; actually used for nothing
2189 core::map<v3s16, MapBlock*> modified_blocks;
2192 u8 mineral = MINERAL_NONE;
2194 bool cannot_remove_node = false;
2198 MapNode n = m_env.getMap().getNode(p_under);
2200 mineral = n.getMineral();
2201 // Get material at position
2203 // If not yet cancelled
2204 if(cannot_remove_node == false)
2206 // If it's not diggable, do nothing
2207 if(content_diggable(material) == false)
2209 derr_server<<"Server: Not finishing digging: "
2210 <<"Node not diggable"
2212 cannot_remove_node = true;
2215 // If not yet cancelled
2216 if(cannot_remove_node == false)
2218 // Get node metadata
2219 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2220 if(meta && meta->nodeRemovalDisabled() == true)
2222 derr_server<<"Server: Not finishing digging: "
2223 <<"Node metadata disables removal"
2225 cannot_remove_node = true;
2229 catch(InvalidPositionException &e)
2231 derr_server<<"Server: Not finishing digging: Node not found."
2232 <<" Adding block to emerge queue."
2234 m_emerge_queue.addBlock(peer_id,
2235 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2236 cannot_remove_node = true;
2240 If node can't be removed, set block to be re-sent to
2243 if(cannot_remove_node)
2245 derr_server<<"Server: Not finishing digging."<<std::endl;
2247 // Client probably has wrong data.
2248 // Set block not sent, so that client will get
2250 dstream<<"Client "<<peer_id<<" tried to dig "
2251 <<"node; but node cannot be removed."
2252 <<" setting MapBlock not sent."<<std::endl;
2253 RemoteClient *client = getClient(peer_id);
2254 v3s16 blockpos = getNodeBlockPos(p_under);
2255 client->SetBlockNotSent(blockpos);
2261 Send the removal to all other clients.
2262 - If other player is close, send REMOVENODE
2263 - Otherwise set blocks not sent
2265 core::list<u16> far_players;
2266 sendRemoveNode(p_under, peer_id, &far_players, 100);
2269 Update and send inventory
2272 if(g_settings.getBool("creative_mode") == false)
2277 InventoryList *mlist = player->inventory.getList("main");
2280 InventoryItem *item = mlist->getItem(item_i);
2281 if(item && (std::string)item->getName() == "ToolItem")
2283 ToolItem *titem = (ToolItem*)item;
2284 std::string toolname = titem->getToolName();
2286 // Get digging properties for material and tool
2287 DiggingProperties prop =
2288 getDiggingProperties(material, toolname);
2290 if(prop.diggable == false)
2292 derr_server<<"Server: WARNING: Player digged"
2293 <<" with impossible material + tool"
2294 <<" combination"<<std::endl;
2297 bool weared_out = titem->addWear(prop.wear);
2301 mlist->deleteItem(item_i);
2307 Add dug item to inventory
2310 InventoryItem *item = NULL;
2312 if(mineral != MINERAL_NONE)
2313 item = getDiggedMineralItem(mineral);
2318 std::string &dug_s = content_features(material).dug_item;
2321 std::istringstream is(dug_s, std::ios::binary);
2322 item = InventoryItem::deSerialize(is);
2328 // Add a item to inventory
2329 player->inventory.addItem("main", item);
2332 UpdateCrafting(player->peer_id);
2333 SendInventory(player->peer_id);
2339 (this takes some time so it is done after the quick stuff)
2341 m_ignore_map_edit_events = true;
2342 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2343 m_ignore_map_edit_events = false;
2346 Set blocks not sent to far players
2348 for(core::list<u16>::Iterator
2349 i = far_players.begin();
2350 i != far_players.end(); i++)
2353 RemoteClient *client = getClient(peer_id);
2356 client->SetBlocksNotSent(modified_blocks);
2363 else if(action == 1)
2366 InventoryList *ilist = player->inventory.getList("main");
2371 InventoryItem *item = ilist->getItem(item_i);
2373 // If there is no item, it is not possible to add it anywhere
2378 Handle material items
2380 if(std::string("MaterialItem") == item->getName())
2383 // Don't add a node if this is not a free space
2384 MapNode n2 = m_env.getMap().getNode(p_over);
2385 if(content_buildable_to(n2.d) == false)
2387 // Client probably has wrong data.
2388 // Set block not sent, so that client will get
2390 dstream<<"Client "<<peer_id<<" tried to place"
2391 <<" node in invalid position; setting"
2392 <<" MapBlock not sent."<<std::endl;
2393 RemoteClient *client = getClient(peer_id);
2394 v3s16 blockpos = getNodeBlockPos(p_over);
2395 client->SetBlockNotSent(blockpos);
2399 catch(InvalidPositionException &e)
2401 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2402 <<" Adding block to emerge queue."
2404 m_emerge_queue.addBlock(peer_id,
2405 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2409 // Reset build time counter
2410 getClient(peer->id)->m_time_from_building = 0.0;
2413 MaterialItem *mitem = (MaterialItem*)item;
2415 n.d = mitem->getMaterial();
2416 if(content_features(n.d).wall_mounted)
2417 n.dir = packDir(p_under - p_over);
2422 core::list<u16> far_players;
2423 sendAddNode(p_over, n, 0, &far_players, 100);
2428 InventoryList *ilist = player->inventory.getList("main");
2429 if(g_settings.getBool("creative_mode") == false && ilist)
2431 // Remove from inventory and send inventory
2432 if(mitem->getCount() == 1)
2433 ilist->deleteItem(item_i);
2437 UpdateCrafting(peer_id);
2438 SendInventory(peer_id);
2444 This takes some time so it is done after the quick stuff
2446 core::map<v3s16, MapBlock*> modified_blocks;
2447 m_ignore_map_edit_events = true;
2448 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2449 m_ignore_map_edit_events = false;
2452 Set blocks not sent to far players
2454 for(core::list<u16>::Iterator
2455 i = far_players.begin();
2456 i != far_players.end(); i++)
2459 RemoteClient *client = getClient(peer_id);
2462 client->SetBlocksNotSent(modified_blocks);
2466 Calculate special events
2469 /*if(n.d == CONTENT_MESE)
2472 for(s16 z=-1; z<=1; z++)
2473 for(s16 y=-1; y<=1; y++)
2474 for(s16 x=-1; x<=1; x++)
2481 Place other item (not a block)
2485 v3s16 blockpos = getNodeBlockPos(p_over);
2488 Check that the block is loaded so that the item
2489 can properly be added to the static list too
2491 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2494 derr_server<<"Error while placing object: "
2495 "block not found"<<std::endl;
2499 dout_server<<"Placing a miscellaneous item on map"
2502 // Calculate a position for it
2503 v3f pos = intToFloat(p_over, BS);
2505 pos.Y -= BS*0.25; // let it drop a bit
2507 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2508 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2513 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2517 derr_server<<"WARNING: item resulted in NULL object, "
2518 <<"not placing onto map"
2523 // Add the object to the environment
2524 m_env.addActiveObject(obj);
2526 dout_server<<"Placed object"<<std::endl;
2528 if(g_settings.getBool("creative_mode") == false)
2530 // Delete the right amount of items from the slot
2531 u16 dropcount = item->getDropCount();
2533 // Delete item if all gone
2534 if(item->getCount() <= dropcount)
2536 if(item->getCount() < dropcount)
2537 dstream<<"WARNING: Server: dropped more items"
2538 <<" than the slot contains"<<std::endl;
2540 InventoryList *ilist = player->inventory.getList("main");
2542 // Remove from inventory and send inventory
2543 ilist->deleteItem(item_i);
2545 // Else decrement it
2547 item->remove(dropcount);
2550 UpdateCrafting(peer_id);
2551 SendInventory(peer_id);
2559 Catch invalid actions
2563 derr_server<<"WARNING: Server: Invalid action "
2564 <<action<<std::endl;
2568 else if(command == TOSERVER_RELEASE)
2577 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2580 else if(command == TOSERVER_SIGNTEXT)
2589 std::string datastring((char*)&data[2], datasize-2);
2590 std::istringstream is(datastring, std::ios_base::binary);
2593 is.read((char*)buf, 6);
2594 v3s16 blockpos = readV3S16(buf);
2595 is.read((char*)buf, 2);
2596 s16 id = readS16(buf);
2597 is.read((char*)buf, 2);
2598 u16 textlen = readU16(buf);
2600 for(u16 i=0; i<textlen; i++)
2602 is.read((char*)buf, 1);
2603 text += (char)buf[0];
2606 MapBlock *block = NULL;
2609 block = m_env.getMap().getBlockNoCreate(blockpos);
2611 catch(InvalidPositionException &e)
2613 derr_server<<"Error while setting sign text: "
2614 "block not found"<<std::endl;
2618 MapBlockObject *obj = block->getObject(id);
2621 derr_server<<"Error while setting sign text: "
2622 "object not found"<<std::endl;
2626 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2628 derr_server<<"Error while setting sign text: "
2629 "object is not a sign"<<std::endl;
2633 ((SignObject*)obj)->setText(text);
2635 obj->getBlock()->setChangedFlag();
2637 else if(command == TOSERVER_SIGNNODETEXT)
2645 std::string datastring((char*)&data[2], datasize-2);
2646 std::istringstream is(datastring, std::ios_base::binary);
2649 is.read((char*)buf, 6);
2650 v3s16 p = readV3S16(buf);
2651 is.read((char*)buf, 2);
2652 u16 textlen = readU16(buf);
2654 for(u16 i=0; i<textlen; i++)
2656 is.read((char*)buf, 1);
2657 text += (char)buf[0];
2660 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2663 if(meta->typeId() != CONTENT_SIGN_WALL)
2665 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2666 signmeta->setText(text);
2668 v3s16 blockpos = getNodeBlockPos(p);
2669 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2672 block->setChangedFlag();
2675 for(core::map<u16, RemoteClient*>::Iterator
2676 i = m_clients.getIterator();
2677 i.atEnd()==false; i++)
2679 RemoteClient *client = i.getNode()->getValue();
2680 client->SetBlockNotSent(blockpos);
2683 else if(command == TOSERVER_INVENTORY_ACTION)
2685 /*// Ignore inventory changes if in creative mode
2686 if(g_settings.getBool("creative_mode") == true)
2688 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2692 // Strip command and create a stream
2693 std::string datastring((char*)&data[2], datasize-2);
2694 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2695 std::istringstream is(datastring, std::ios_base::binary);
2697 InventoryAction *a = InventoryAction::deSerialize(is);
2702 c.current_player = player;
2705 Handle craftresult specially if not in creative mode
2707 bool disable_action = false;
2708 if(a->getType() == IACTION_MOVE
2709 && g_settings.getBool("creative_mode") == false)
2711 IMoveAction *ma = (IMoveAction*)a;
2712 if(ma->to_inv == "current_player" &&
2713 ma->from_inv == "current_player")
2715 InventoryList *rlist = player->inventory.getList("craftresult");
2717 InventoryList *clist = player->inventory.getList("craft");
2719 InventoryList *mlist = player->inventory.getList("main");
2722 Craftresult is no longer preview if something
2725 if(ma->to_list == "craftresult"
2726 && ma->from_list != "craftresult")
2728 // If it currently is a preview, remove
2730 if(player->craftresult_is_preview)
2732 rlist->deleteItem(0);
2734 player->craftresult_is_preview = false;
2737 Crafting takes place if this condition is true.
2739 if(player->craftresult_is_preview &&
2740 ma->from_list == "craftresult")
2742 player->craftresult_is_preview = false;
2743 clist->decrementMaterials(1);
2746 If the craftresult is placed on itself, move it to
2747 main inventory instead of doing the action
2749 if(ma->to_list == "craftresult"
2750 && ma->from_list == "craftresult")
2752 disable_action = true;
2754 InventoryItem *item1 = rlist->changeItem(0, NULL);
2755 mlist->addItem(item1);
2760 if(disable_action == false)
2762 // Feed action to player inventory
2770 UpdateCrafting(player->peer_id);
2771 SendInventory(player->peer_id);
2776 dstream<<"TOSERVER_INVENTORY_ACTION: "
2777 <<"InventoryAction::deSerialize() returned NULL"
2781 else if(command == TOSERVER_CHAT_MESSAGE)
2789 std::string datastring((char*)&data[2], datasize-2);
2790 std::istringstream is(datastring, std::ios_base::binary);
2793 is.read((char*)buf, 2);
2794 u16 len = readU16(buf);
2796 std::wstring message;
2797 for(u16 i=0; i<len; i++)
2799 is.read((char*)buf, 2);
2800 message += (wchar_t)readU16(buf);
2803 // Get player name of this client
2804 std::wstring name = narrow_to_wide(player->getName());
2806 // Line to send to players
2808 // Whether to send to the player that sent the line
2809 bool send_to_sender = false;
2810 // Whether to send to other players
2811 bool send_to_others = false;
2814 std::wstring commandprefix = L"/#";
2815 if(message.substr(0, commandprefix.size()) == commandprefix)
2817 line += L"Server: ";
2819 message = message.substr(commandprefix.size());
2820 // Get player name as narrow string
2821 std::string name_s = player->getName();
2822 // Convert message to narrow string
2823 std::string message_s = wide_to_narrow(message);
2824 // Operator is the single name defined in config.
2825 std::string operator_name = g_settings.get("name");
2826 bool is_operator = (operator_name != "" &&
2827 wide_to_narrow(name) == operator_name);
2828 bool valid_command = false;
2829 if(message_s == "help")
2831 line += L"-!- Available commands: ";
2835 line += L"shutdown setting ";
2840 send_to_sender = true;
2841 valid_command = true;
2843 else if(message_s == "status")
2845 line = getStatusString();
2846 send_to_sender = true;
2847 valid_command = true;
2849 else if(is_operator)
2851 if(message_s == "shutdown")
2853 dstream<<DTIME<<" Server: Operator requested shutdown."
2855 m_shutdown_requested.set(true);
2857 line += L"*** Server shutting down (operator request)";
2858 send_to_sender = true;
2859 valid_command = true;
2861 else if(message_s.substr(0,8) == "setting ")
2863 std::string confline = message_s.substr(8);
2864 g_settings.parseConfigLine(confline);
2865 line += L"-!- Setting changed.";
2866 send_to_sender = true;
2867 valid_command = true;
2871 if(valid_command == false)
2873 line += L"-!- Invalid command: " + message;
2874 send_to_sender = true;
2885 send_to_others = true;
2890 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2893 Send the message to clients
2895 for(core::map<u16, RemoteClient*>::Iterator
2896 i = m_clients.getIterator();
2897 i.atEnd() == false; i++)
2899 // Get client and check that it is valid
2900 RemoteClient *client = i.getNode()->getValue();
2901 assert(client->peer_id == i.getNode()->getKey());
2902 if(client->serialization_version == SER_FMT_VER_INVALID)
2906 bool sender_selected = (peer_id == client->peer_id);
2907 if(sender_selected == true && send_to_sender == false)
2909 if(sender_selected == false && send_to_others == false)
2912 SendChatMessage(client->peer_id, line);
2916 else if(command == TOSERVER_DAMAGE)
2918 if(g_settings.getBool("enable_damage"))
2920 std::string datastring((char*)&data[2], datasize-2);
2921 std::istringstream is(datastring, std::ios_base::binary);
2922 u8 damage = readU8(is);
2923 if(player->hp > damage)
2925 player->hp -= damage;
2931 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2934 v3f pos = findSpawnPos(m_env.getServerMap());
2935 player->setPosition(pos);
2937 SendMovePlayer(player);
2938 SendPlayerHP(player);
2940 //TODO: Throw items around
2944 SendPlayerHP(player);
2948 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2949 "unknown command "<<command<<std::endl;
2953 catch(SendFailedException &e)
2955 derr_server<<"Server::ProcessData(): SendFailedException: "
2961 void Server::onMapEditEvent(MapEditEvent *event)
2963 dstream<<"Server::onMapEditEvent()"<<std::endl;
2964 if(m_ignore_map_edit_events)
2966 MapEditEvent *e = event->clone();
2967 m_unsent_map_edit_queue.push_back(e);
2970 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2972 if(id == "current_player")
2974 assert(c->current_player);
2975 return &(c->current_player->inventory);
2979 std::string id0 = fn.next(":");
2981 if(id0 == "nodemeta")
2984 p.X = stoi(fn.next(","));
2985 p.Y = stoi(fn.next(","));
2986 p.Z = stoi(fn.next(","));
2987 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2989 return meta->getInventory();
2990 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2991 <<"no metadata found"<<std::endl;
2995 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2998 void Server::inventoryModified(InventoryContext *c, std::string id)
3000 if(id == "current_player")
3002 assert(c->current_player);
3004 UpdateCrafting(c->current_player->peer_id);
3005 SendInventory(c->current_player->peer_id);
3010 std::string id0 = fn.next(":");
3012 if(id0 == "nodemeta")
3015 p.X = stoi(fn.next(","));
3016 p.Y = stoi(fn.next(","));
3017 p.Z = stoi(fn.next(","));
3018 v3s16 blockpos = getNodeBlockPos(p);
3020 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3022 meta->inventoryModified();
3024 for(core::map<u16, RemoteClient*>::Iterator
3025 i = m_clients.getIterator();
3026 i.atEnd()==false; i++)
3028 RemoteClient *client = i.getNode()->getValue();
3029 client->SetBlockNotSent(blockpos);
3035 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3038 core::list<PlayerInfo> Server::getPlayerInfo()
3040 DSTACK(__FUNCTION_NAME);
3041 JMutexAutoLock envlock(m_env_mutex);
3042 JMutexAutoLock conlock(m_con_mutex);
3044 core::list<PlayerInfo> list;
3046 core::list<Player*> players = m_env.getPlayers();
3048 core::list<Player*>::Iterator i;
3049 for(i = players.begin();
3050 i != players.end(); i++)
3054 Player *player = *i;
3057 con::Peer *peer = m_con.GetPeer(player->peer_id);
3058 // Copy info from peer to info struct
3060 info.address = peer->address;
3061 info.avg_rtt = peer->avg_rtt;
3063 catch(con::PeerNotFoundException &e)
3065 // Set dummy peer info
3067 info.address = Address(0,0,0,0,0);
3071 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3072 info.position = player->getPosition();
3074 list.push_back(info);
3081 void Server::peerAdded(con::Peer *peer)
3083 DSTACK(__FUNCTION_NAME);
3084 dout_server<<"Server::peerAdded(): peer->id="
3085 <<peer->id<<std::endl;
3088 c.type = PEER_ADDED;
3089 c.peer_id = peer->id;
3091 m_peer_change_queue.push_back(c);
3094 void Server::deletingPeer(con::Peer *peer, bool timeout)
3096 DSTACK(__FUNCTION_NAME);
3097 dout_server<<"Server::deletingPeer(): peer->id="
3098 <<peer->id<<", timeout="<<timeout<<std::endl;
3101 c.type = PEER_REMOVED;
3102 c.peer_id = peer->id;
3103 c.timeout = timeout;
3104 m_peer_change_queue.push_back(c);
3111 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3113 DSTACK(__FUNCTION_NAME);
3114 std::ostringstream os(std::ios_base::binary);
3116 writeU16(os, TOCLIENT_HP);
3120 std::string s = os.str();
3121 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3123 con.Send(peer_id, 0, data, true);
3127 Non-static send methods
3130 void Server::SendObjectData(float dtime)
3132 DSTACK(__FUNCTION_NAME);
3134 core::map<v3s16, bool> stepped_blocks;
3136 for(core::map<u16, RemoteClient*>::Iterator
3137 i = m_clients.getIterator();
3138 i.atEnd() == false; i++)
3140 u16 peer_id = i.getNode()->getKey();
3141 RemoteClient *client = i.getNode()->getValue();
3142 assert(client->peer_id == peer_id);
3144 if(client->serialization_version == SER_FMT_VER_INVALID)
3147 client->SendObjectData(this, dtime, stepped_blocks);
3151 void Server::SendPlayerInfos()
3153 DSTACK(__FUNCTION_NAME);
3155 //JMutexAutoLock envlock(m_env_mutex);
3157 // Get connected players
3158 core::list<Player*> players = m_env.getPlayers(true);
3160 u32 player_count = players.getSize();
3161 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3163 SharedBuffer<u8> data(datasize);
3164 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3167 core::list<Player*>::Iterator i;
3168 for(i = players.begin();
3169 i != players.end(); i++)
3171 Player *player = *i;
3173 /*dstream<<"Server sending player info for player with "
3174 "peer_id="<<player->peer_id<<std::endl;*/
3176 writeU16(&data[start], player->peer_id);
3177 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3178 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3179 start += 2+PLAYERNAME_SIZE;
3182 //JMutexAutoLock conlock(m_con_mutex);
3185 m_con.SendToAll(0, data, true);
3188 void Server::SendInventory(u16 peer_id)
3190 DSTACK(__FUNCTION_NAME);
3192 Player* player = m_env.getPlayer(peer_id);
3199 std::ostringstream os;
3200 //os.imbue(std::locale("C"));
3202 player->inventory.serialize(os);
3204 std::string s = os.str();
3206 SharedBuffer<u8> data(s.size()+2);
3207 writeU16(&data[0], TOCLIENT_INVENTORY);
3208 memcpy(&data[2], s.c_str(), s.size());
3211 m_con.Send(peer_id, 0, data, true);
3214 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3216 DSTACK(__FUNCTION_NAME);
3218 std::ostringstream os(std::ios_base::binary);
3222 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3223 os.write((char*)buf, 2);
3226 writeU16(buf, message.size());
3227 os.write((char*)buf, 2);
3230 for(u32 i=0; i<message.size(); i++)
3234 os.write((char*)buf, 2);
3238 std::string s = os.str();
3239 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3241 m_con.Send(peer_id, 0, data, true);
3244 void Server::BroadcastChatMessage(const std::wstring &message)
3246 for(core::map<u16, RemoteClient*>::Iterator
3247 i = m_clients.getIterator();
3248 i.atEnd() == false; i++)
3250 // Get client and check that it is valid
3251 RemoteClient *client = i.getNode()->getValue();
3252 assert(client->peer_id == i.getNode()->getKey());
3253 if(client->serialization_version == SER_FMT_VER_INVALID)
3256 SendChatMessage(client->peer_id, message);
3260 void Server::SendPlayerHP(Player *player)
3262 SendHP(m_con, player->peer_id, player->hp);
3265 void Server::SendMovePlayer(Player *player)
3267 DSTACK(__FUNCTION_NAME);
3268 std::ostringstream os(std::ios_base::binary);
3270 writeU16(os, TOCLIENT_MOVE_PLAYER);
3271 writeV3F1000(os, player->getPosition());
3272 writeF1000(os, player->getPitch());
3273 writeF1000(os, player->getYaw());
3276 v3f pos = player->getPosition();
3277 f32 pitch = player->getPitch();
3278 f32 yaw = player->getYaw();
3279 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3280 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3287 std::string s = os.str();
3288 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3290 m_con.Send(player->peer_id, 0, data, true);
3293 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3294 core::list<u16> *far_players, float far_d_nodes)
3296 float maxd = far_d_nodes*BS;
3297 v3f p_f = intToFloat(p, BS);
3301 SharedBuffer<u8> reply(replysize);
3302 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3303 writeS16(&reply[2], p.X);
3304 writeS16(&reply[4], p.Y);
3305 writeS16(&reply[6], p.Z);
3307 for(core::map<u16, RemoteClient*>::Iterator
3308 i = m_clients.getIterator();
3309 i.atEnd() == false; i++)
3311 // Get client and check that it is valid
3312 RemoteClient *client = i.getNode()->getValue();
3313 assert(client->peer_id == i.getNode()->getKey());
3314 if(client->serialization_version == SER_FMT_VER_INVALID)
3317 // Don't send if it's the same one
3318 if(client->peer_id == ignore_id)
3324 Player *player = m_env.getPlayer(client->peer_id);
3327 // If player is far away, only set modified blocks not sent
3328 v3f player_pos = player->getPosition();
3329 if(player_pos.getDistanceFrom(p_f) > maxd)
3331 far_players->push_back(client->peer_id);
3338 m_con.Send(client->peer_id, 0, reply, true);
3342 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3343 core::list<u16> *far_players, float far_d_nodes)
3345 float maxd = far_d_nodes*BS;
3346 v3f p_f = intToFloat(p, BS);
3348 for(core::map<u16, RemoteClient*>::Iterator
3349 i = m_clients.getIterator();
3350 i.atEnd() == false; i++)
3352 // Get client and check that it is valid
3353 RemoteClient *client = i.getNode()->getValue();
3354 assert(client->peer_id == i.getNode()->getKey());
3355 if(client->serialization_version == SER_FMT_VER_INVALID)
3358 // Don't send if it's the same one
3359 if(client->peer_id == ignore_id)
3365 Player *player = m_env.getPlayer(client->peer_id);
3368 // If player is far away, only set modified blocks not sent
3369 v3f player_pos = player->getPosition();
3370 if(player_pos.getDistanceFrom(p_f) > maxd)
3372 far_players->push_back(client->peer_id);
3379 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3380 SharedBuffer<u8> reply(replysize);
3381 writeU16(&reply[0], TOCLIENT_ADDNODE);
3382 writeS16(&reply[2], p.X);
3383 writeS16(&reply[4], p.Y);
3384 writeS16(&reply[6], p.Z);
3385 n.serialize(&reply[8], client->serialization_version);
3388 m_con.Send(client->peer_id, 0, reply, true);
3392 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3394 DSTACK(__FUNCTION_NAME);
3396 Create a packet with the block in the right format
3399 std::ostringstream os(std::ios_base::binary);
3400 block->serialize(os, ver);
3401 std::string s = os.str();
3402 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3404 u32 replysize = 8 + blockdata.getSize();
3405 SharedBuffer<u8> reply(replysize);
3406 v3s16 p = block->getPos();
3407 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3408 writeS16(&reply[2], p.X);
3409 writeS16(&reply[4], p.Y);
3410 writeS16(&reply[6], p.Z);
3411 memcpy(&reply[8], *blockdata, blockdata.getSize());
3413 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3414 <<": \tpacket size: "<<replysize<<std::endl;*/
3419 m_con.Send(peer_id, 1, reply, true);
3422 void Server::SendBlocks(float dtime)
3424 DSTACK(__FUNCTION_NAME);
3426 JMutexAutoLock envlock(m_env_mutex);
3427 JMutexAutoLock conlock(m_con_mutex);
3429 //TimeTaker timer("Server::SendBlocks");
3431 core::array<PrioritySortedBlockTransfer> queue;
3433 s32 total_sending = 0;
3435 for(core::map<u16, RemoteClient*>::Iterator
3436 i = m_clients.getIterator();
3437 i.atEnd() == false; i++)
3439 RemoteClient *client = i.getNode()->getValue();
3440 assert(client->peer_id == i.getNode()->getKey());
3442 total_sending += client->SendingCount();
3444 if(client->serialization_version == SER_FMT_VER_INVALID)
3447 client->GetNextBlocks(this, dtime, queue);
3451 // Lowest priority number comes first.
3452 // Lowest is most important.
3455 for(u32 i=0; i<queue.size(); i++)
3457 //TODO: Calculate limit dynamically
3458 if(total_sending >= g_settings.getS32
3459 ("max_simultaneous_block_sends_server_total"))
3462 PrioritySortedBlockTransfer q = queue[i];
3464 MapBlock *block = NULL;
3467 block = m_env.getMap().getBlockNoCreate(q.pos);
3469 catch(InvalidPositionException &e)
3474 RemoteClient *client = getClient(q.peer_id);
3476 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3478 client->SentBlock(q.pos);
3488 void Server::UpdateCrafting(u16 peer_id)
3490 DSTACK(__FUNCTION_NAME);
3492 Player* player = m_env.getPlayer(peer_id);
3496 Calculate crafting stuff
3498 if(g_settings.getBool("creative_mode") == false)
3500 InventoryList *clist = player->inventory.getList("craft");
3501 InventoryList *rlist = player->inventory.getList("craftresult");
3503 if(rlist->getUsedSlots() == 0)
3504 player->craftresult_is_preview = true;
3506 if(rlist && player->craftresult_is_preview)
3508 rlist->clearItems();
3510 if(clist && rlist && player->craftresult_is_preview)
3512 InventoryItem *items[9];
3513 for(u16 i=0; i<9; i++)
3515 items[i] = clist->getItem(i);
3524 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3525 if(checkItemCombination(items, specs))
3527 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3536 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3537 if(checkItemCombination(items, specs))
3539 rlist->addItem(new CraftItem("Stick", 4));
3548 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3549 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3550 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3551 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3552 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3553 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3554 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3555 if(checkItemCombination(items, specs))
3557 //rlist->addItem(new MapBlockObjectItem("Sign"));
3558 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3567 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3568 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3569 if(checkItemCombination(items, specs))
3571 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3580 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3581 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3584 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3585 if(checkItemCombination(items, specs))
3587 rlist->addItem(new ToolItem("WPick", 0));
3596 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3597 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3598 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3599 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3600 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3601 if(checkItemCombination(items, specs))
3603 rlist->addItem(new ToolItem("STPick", 0));
3612 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3613 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3614 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3615 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3616 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3617 if(checkItemCombination(items, specs))
3619 rlist->addItem(new ToolItem("SteelPick", 0));
3628 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3629 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3630 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3631 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3632 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3633 if(checkItemCombination(items, specs))
3635 rlist->addItem(new ToolItem("MesePick", 0));
3644 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3645 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3646 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3647 if(checkItemCombination(items, specs))
3649 rlist->addItem(new ToolItem("WShovel", 0));
3658 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3659 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3660 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3661 if(checkItemCombination(items, specs))
3663 rlist->addItem(new ToolItem("STShovel", 0));
3672 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3673 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3674 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3675 if(checkItemCombination(items, specs))
3677 rlist->addItem(new ToolItem("SteelShovel", 0));
3686 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3687 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3688 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3689 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3690 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3691 if(checkItemCombination(items, specs))
3693 rlist->addItem(new ToolItem("WAxe", 0));
3702 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3703 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3704 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3705 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3706 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3707 if(checkItemCombination(items, specs))
3709 rlist->addItem(new ToolItem("STAxe", 0));
3718 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3719 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3720 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3721 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3722 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3723 if(checkItemCombination(items, specs))
3725 rlist->addItem(new ToolItem("SteelAxe", 0));
3734 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3735 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3736 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3737 if(checkItemCombination(items, specs))
3739 rlist->addItem(new ToolItem("WSword", 0));
3748 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3749 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3750 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3751 if(checkItemCombination(items, specs))
3753 rlist->addItem(new ToolItem("STSword", 0));
3762 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3763 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3764 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3765 if(checkItemCombination(items, specs))
3767 rlist->addItem(new ToolItem("SteelSword", 0));
3776 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3777 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3778 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3779 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3780 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3781 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3782 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3783 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3784 if(checkItemCombination(items, specs))
3786 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3795 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3796 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3797 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3798 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3799 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3800 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3801 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3802 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3803 if(checkItemCombination(items, specs))
3805 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3814 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3815 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3816 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3817 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3818 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3819 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3820 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3821 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3822 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3823 if(checkItemCombination(items, specs))
3825 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3831 } // if creative_mode == false
3834 RemoteClient* Server::getClient(u16 peer_id)
3836 DSTACK(__FUNCTION_NAME);
3837 //JMutexAutoLock lock(m_con_mutex);
3838 core::map<u16, RemoteClient*>::Node *n;
3839 n = m_clients.find(peer_id);
3840 // A client should exist for all peers
3842 return n->getValue();
3845 std::wstring Server::getStatusString()
3847 std::wostringstream os(std::ios_base::binary);
3850 os<<L"uptime="<<m_uptime.get();
3851 // Information about clients
3853 for(core::map<u16, RemoteClient*>::Iterator
3854 i = m_clients.getIterator();
3855 i.atEnd() == false; i++)
3857 // Get client and check that it is valid
3858 RemoteClient *client = i.getNode()->getValue();
3859 assert(client->peer_id == i.getNode()->getKey());
3860 if(client->serialization_version == SER_FMT_VER_INVALID)
3863 Player *player = m_env.getPlayer(client->peer_id);
3864 // Get name of player
3865 std::wstring name = L"unknown";
3867 name = narrow_to_wide(player->getName());
3868 // Add name to information string
3872 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3873 os<<" WARNING: Map saving is disabled."<<std::endl;
3878 void setCreativeInventory(Player *player)
3880 player->resetInventory();
3882 // Give some good tools
3884 InventoryItem *item = new ToolItem("MesePick", 0);
3885 void* r = player->inventory.addItem("main", item);
3889 InventoryItem *item = new ToolItem("SteelPick", 0);
3890 void* r = player->inventory.addItem("main", item);
3894 InventoryItem *item = new ToolItem("SteelAxe", 0);
3895 void* r = player->inventory.addItem("main", item);
3899 InventoryItem *item = new ToolItem("SteelShovel", 0);
3900 void* r = player->inventory.addItem("main", item);
3908 // CONTENT_IGNORE-terminated list
3909 u8 material_items[] = {
3918 CONTENT_WATERSOURCE,
3926 u8 *mip = material_items;
3927 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3929 if(*mip == CONTENT_IGNORE)
3932 InventoryItem *item = new MaterialItem(*mip, 1);
3933 player->inventory.addItem("main", item);
3939 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3942 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3943 player->inventory.addItem("main", item);
3946 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3948 // Skip some materials
3949 if(i == CONTENT_WATER || i == CONTENT_TORCH
3950 || i == CONTENT_COALSTONE)
3953 InventoryItem *item = new MaterialItem(i, 1);
3954 player->inventory.addItem("main", item);
3960 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3961 void* r = player->inventory.addItem("main", item);
3966 v3f findSpawnPos(ServerMap &map)
3969 s16 groundheight = 0;
3971 // Try to find a good place a few times
3972 for(s32 i=0; i<1000; i++)
3975 // We're going to try to throw the player to this position
3976 nodepos = v2s16(-range + (myrand()%(range*2)),
3977 -range + (myrand()%(range*2)));
3978 v2s16 sectorpos = getNodeSectorPos(nodepos);
3979 // Get sector (NOTE: Don't get because it's slow)
3980 //m_env.getMap().emergeSector(sectorpos);
3981 // Get ground height at point (fallbacks to heightmap function)
3982 groundheight = map.findGroundLevel(nodepos);
3983 // Don't go underwater
3984 if(groundheight < WATER_LEVEL)
3986 //dstream<<"-> Underwater"<<std::endl;
3989 // Don't go to high places
3990 if(groundheight > WATER_LEVEL + 4)
3992 //dstream<<"-> Underwater"<<std::endl;
3996 // Found a good place
3997 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4001 // If no suitable place was not found, go above water at least.
4002 if(groundheight < WATER_LEVEL)
4003 groundheight = WATER_LEVEL;
4005 return intToFloat(v3s16(
4012 Player *Server::emergePlayer(const char *name, const char *password,
4016 Try to get an existing player
4018 Player *player = m_env.getPlayer(name);
4021 // If player is already connected, cancel
4022 if(player->peer_id != 0)
4024 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4029 player->peer_id = peer_id;
4031 // Reset inventory to creative if in creative mode
4032 if(g_settings.getBool("creative_mode"))
4034 setCreativeInventory(player);
4041 If player with the wanted peer_id already exists, cancel.
4043 if(m_env.getPlayer(peer_id) != NULL)
4045 dstream<<"emergePlayer(): Player with wrong name but same"
4046 " peer_id already exists"<<std::endl;
4054 player = new ServerRemotePlayer();
4055 //player->peer_id = c.peer_id;
4056 //player->peer_id = PEER_ID_INEXISTENT;
4057 player->peer_id = peer_id;
4058 player->updateName(name);
4064 dstream<<"Server: Finding spawn place for player \""
4065 <<player->getName()<<"\""<<std::endl;
4067 v3f pos = findSpawnPos(m_env.getServerMap());
4069 player->setPosition(pos);
4072 Add player to environment
4075 m_env.addPlayer(player);
4078 Add stuff to inventory
4081 if(g_settings.getBool("creative_mode"))
4083 setCreativeInventory(player);
4088 InventoryItem *item = new ToolItem("WPick", 32000);
4089 void* r = player->inventory.addItem("main", item);
4093 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4094 void* r = player->inventory.addItem("main", item);
4098 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4099 void* r = player->inventory.addItem("main", item);
4103 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4104 void* r = player->inventory.addItem("main", item);
4108 InventoryItem *item = new CraftItem("Stick", 4);
4109 void* r = player->inventory.addItem("main", item);
4113 InventoryItem *item = new ToolItem("WPick", 32000);
4114 void* r = player->inventory.addItem("main", item);
4118 InventoryItem *item = new ToolItem("STPick", 32000);
4119 void* r = player->inventory.addItem("main", item);
4122 /*// Give some lights
4124 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
4125 bool r = player->inventory.addItem("main", item);
4129 for(u16 i=0; i<4; i++)
4131 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4132 bool r = player->inventory.addItem("main", item);
4135 /*// Give some other stuff
4137 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4138 bool r = player->inventory.addItem("main", item);
4145 } // create new player
4148 void Server::handlePeerChange(PeerChange &c)
4150 JMutexAutoLock envlock(m_env_mutex);
4151 JMutexAutoLock conlock(m_con_mutex);
4153 if(c.type == PEER_ADDED)
4160 core::map<u16, RemoteClient*>::Node *n;
4161 n = m_clients.find(c.peer_id);
4162 // The client shouldn't already exist
4166 RemoteClient *client = new RemoteClient();
4167 client->peer_id = c.peer_id;
4168 m_clients.insert(client->peer_id, client);
4171 else if(c.type == PEER_REMOVED)
4178 core::map<u16, RemoteClient*>::Node *n;
4179 n = m_clients.find(c.peer_id);
4180 // The client should exist
4183 // Collect information about leaving in chat
4184 std::wstring message;
4186 std::wstring name = L"unknown";
4187 Player *player = m_env.getPlayer(c.peer_id);
4189 name = narrow_to_wide(player->getName());
4193 message += L" left game";
4195 message += L" (timed out)";
4200 m_env.removePlayer(c.peer_id);
4203 // Set player client disconnected
4205 Player *player = m_env.getPlayer(c.peer_id);
4207 player->peer_id = 0;
4211 delete m_clients[c.peer_id];
4212 m_clients.remove(c.peer_id);
4214 // Send player info to all remaining clients
4217 // Send leave chat message to all remaining clients
4218 BroadcastChatMessage(message);
4227 void Server::handlePeerChanges()
4229 while(m_peer_change_queue.size() > 0)
4231 PeerChange c = m_peer_change_queue.pop_front();
4233 dout_server<<"Server: Handling peer change: "
4234 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4237 handlePeerChange(c);
4241 void dedicated_server_loop(Server &server, bool &kill)
4243 DSTACK(__FUNCTION_NAME);
4245 std::cout<<DTIME<<std::endl;
4246 std::cout<<"========================"<<std::endl;
4247 std::cout<<"Running dedicated server"<<std::endl;
4248 std::cout<<"========================"<<std::endl;
4249 std::cout<<std::endl;
4253 // This is kind of a hack but can be done like this
4254 // because server.step() is very light
4258 if(server.getShutdownRequested() || kill)
4260 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4264 static int counter = 0;
4270 core::list<PlayerInfo> list = server.getPlayerInfo();
4271 core::list<PlayerInfo>::Iterator i;
4272 static u32 sum_old = 0;
4273 u32 sum = PIChecksum(list);
4276 std::cout<<DTIME<<"Player info:"<<std::endl;
4277 for(i=list.begin(); i!=list.end(); i++)
4279 i->PrintLine(&std::cout);