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);
95 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
97 //TimeTaker timer("block emerge");
100 Try to emerge it from somewhere.
102 If it is only wanted as optional, only loading from disk
107 Check if any peer wants it as non-optional. In that case it
110 Also decrement the emerge queue count in clients.
113 bool optional = true;
116 core::map<u16, u8>::Iterator i;
117 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
119 //u16 peer_id = i.getNode()->getKey();
122 u8 flags = i.getNode()->getValue();
123 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
129 /*dstream<<"EmergeThread: p="
130 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
131 <<"optional="<<optional<<std::endl;*/
133 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
135 core::map<v3s16, MapBlock*> changed_blocks;
136 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
138 MapBlock *block = NULL;
139 bool got_block = true;
140 core::map<v3s16, MapBlock*> modified_blocks;
142 bool only_from_disk = false;
145 only_from_disk = true;
148 TODO: Map loading logic here, so that the chunk can be
149 generated asynchronously:
152 With the environment locked:
153 - Check if block already is loaded and not dummy
160 //TimeTaker envlockwaittimer("block emerge envlock wait time");
163 JMutexAutoLock envlock(m_server->m_env_mutex);
165 //envlockwaittimer.stop();
167 //TimeTaker timer("block emerge (while env locked)");
171 // First check if the block already exists
172 //block = map.getBlockNoCreate(p);
176 //dstream<<"Calling emergeBlock"<<std::endl;
177 block = map.emergeBlock(
181 lighting_invalidated_blocks);
184 // If it is a dummy, block was not found on disk
187 //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
190 if(only_from_disk == false)
192 dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
197 catch(InvalidPositionException &e)
200 // This happens when position is over limit.
206 if(debug && changed_blocks.size() > 0)
208 dout_server<<DTIME<<"Got changed_blocks: ";
209 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
210 i.atEnd() == false; i++)
212 MapBlock *block = i.getNode()->getValue();
213 v3s16 p = block->getPos();
214 dout_server<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
216 dout_server<<std::endl;
220 Collect a list of blocks that have been modified in
221 addition to the fetched one.
224 if(lighting_invalidated_blocks.size() > 0)
226 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
227 <<" blocks"<<std::endl;*/
229 // 50-100ms for single block generation
230 //TimeTaker timer("** EmergeThread updateLighting");
232 // Update lighting without locking the environment mutex,
233 // add modified blocks to changed blocks
234 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
237 // Add all from changed_blocks to modified_blocks
238 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
239 i.atEnd() == false; i++)
241 MapBlock *block = i.getNode()->getValue();
242 modified_blocks.insert(block->getPos(), block);
245 // If we got no block, there should be no invalidated blocks
248 assert(lighting_invalidated_blocks.size() == 0);
254 Set sent status of modified blocks on clients
257 // NOTE: Server's clients are also behind the connection mutex
258 JMutexAutoLock lock(m_server->m_con_mutex);
261 Add the originally fetched block to the modified list
265 modified_blocks.insert(p, block);
269 Set the modified blocks unsent for all the clients
272 for(core::map<u16, RemoteClient*>::Iterator
273 i = m_server->m_clients.getIterator();
274 i.atEnd() == false; i++)
276 RemoteClient *client = i.getNode()->getValue();
278 if(modified_blocks.size() > 0)
280 // Remove block from sent history
281 client->SetBlocksNotSent(modified_blocks);
287 END_DEBUG_EXCEPTION_HANDLER
292 void RemoteClient::GetNextBlocks(Server *server, float dtime,
293 core::array<PrioritySortedBlockTransfer> &dest)
295 DSTACK(__FUNCTION_NAME);
298 m_nearest_unsent_reset_timer += dtime;
300 // Won't send anything if already sending
301 if(m_blocks_sending.size() >= g_settings.getU16
302 ("max_simultaneous_block_sends_per_client"))
304 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
308 Player *player = server->m_env.getPlayer(peer_id);
310 assert(player != NULL);
312 v3f playerpos = player->getPosition();
313 v3f playerspeed = player->getSpeed();
315 v3s16 center_nodepos = floatToInt(playerpos, BS);
317 v3s16 center = getNodeBlockPos(center_nodepos);
319 // Camera position and direction
321 playerpos + v3f(0, BS+BS/2, 0);
322 v3f camera_dir = v3f(0,0,1);
323 camera_dir.rotateYZBy(player->getPitch());
324 camera_dir.rotateXZBy(player->getYaw());
327 Get the starting value of the block finder radius.
329 s16 last_nearest_unsent_d;
332 if(m_last_center != center)
334 m_nearest_unsent_d = 0;
335 m_last_center = center;
338 /*dstream<<"m_nearest_unsent_reset_timer="
339 <<m_nearest_unsent_reset_timer<<std::endl;*/
340 if(m_nearest_unsent_reset_timer > 5.0)
342 m_nearest_unsent_reset_timer = 0;
343 m_nearest_unsent_d = 0;
344 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
347 last_nearest_unsent_d = m_nearest_unsent_d;
349 d_start = m_nearest_unsent_d;
351 u16 maximum_simultaneous_block_sends_setting = g_settings.getU16
352 ("max_simultaneous_block_sends_per_client");
353 u16 maximum_simultaneous_block_sends =
354 maximum_simultaneous_block_sends_setting;
357 Check the time from last addNode/removeNode.
359 Decrease send rate if player is building stuff.
361 m_time_from_building += dtime;
362 if(m_time_from_building < g_settings.getFloat(
363 "full_block_send_enable_min_time_from_building"))
365 maximum_simultaneous_block_sends
366 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
369 u32 num_blocks_selected = m_blocks_sending.size();
372 next time d will be continued from the d from which the nearest
373 unsent block was found this time.
375 This is because not necessarily any of the blocks found this
376 time are actually sent.
378 s32 new_nearest_unsent_d = -1;
380 s16 d_max = g_settings.getS16("max_block_send_distance");
381 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
383 //dstream<<"Starting from "<<d_start<<std::endl;
385 for(s16 d = d_start; d <= d_max; d++)
387 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
390 If m_nearest_unsent_d was changed by the EmergeThread
391 (it can change it to 0 through SetBlockNotSent),
393 Else update m_nearest_unsent_d
395 if(m_nearest_unsent_d != last_nearest_unsent_d)
397 d = m_nearest_unsent_d;
398 last_nearest_unsent_d = m_nearest_unsent_d;
402 Get the border/face dot coordinates of a "d-radiused"
405 core::list<v3s16> list;
406 getFacePositions(list, d);
408 core::list<v3s16>::Iterator li;
409 for(li=list.begin(); li!=list.end(); li++)
411 v3s16 p = *li + center;
415 - Don't allow too many simultaneous transfers
416 - EXCEPT when the blocks are very close
418 Also, don't send blocks that are already flying.
421 u16 maximum_simultaneous_block_sends_now =
422 maximum_simultaneous_block_sends;
424 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
426 maximum_simultaneous_block_sends_now =
427 maximum_simultaneous_block_sends_setting;
430 // Limit is dynamically lowered when building
431 if(num_blocks_selected
432 >= maximum_simultaneous_block_sends_now)
434 /*dstream<<"Not sending more blocks. Queue full. "
435 <<m_blocks_sending.size()
440 if(m_blocks_sending.find(p) != NULL)
446 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
447 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
448 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
449 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
450 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
451 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
454 // If this is true, inexistent block will be made from scratch
455 bool generate = d <= d_max_gen;
458 /*// Limit the generating area vertically to 2/3
459 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
462 // Limit the send area vertically to 2/3
463 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
469 If block is far away, don't generate it unless it is
472 NOTE: We can't know the ground level this way with the
478 MapSector *sector = NULL;
481 sector = server->m_env.getMap().getSectorNoGenerate(p2d);
483 catch(InvalidPositionException &e)
489 // Get center ground height in nodes
490 f32 gh = sector->getGroundHeight(
491 v2s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2));
492 // Block center y in nodes
493 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
494 // If differs a lot, don't generate
495 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
502 Don't generate or send if not in sight
505 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
511 Don't send already sent blocks
514 if(m_blocks_sent.find(p) != NULL)
519 Check if map has this block
521 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
523 bool surely_not_found_on_disk = false;
524 bool block_is_invalid = false;
529 surely_not_found_on_disk = true;
532 if(block->isValid() == false)
534 block_is_invalid = true;
537 /*if(block->isFullyGenerated() == false)
539 block_is_invalid = true;
543 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
544 v2s16 chunkpos = map->sector_to_chunk(p2d);
545 if(map->chunkNonVolatile(chunkpos) == false)
546 block_is_invalid = true;
550 If block has been marked to not exist on disk (dummy)
551 and generating new ones is not wanted, skip block.
553 if(generate == false && surely_not_found_on_disk == true)
560 Record the lowest d from which a a block has been
561 found being not sent and possibly to exist
563 if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
565 new_nearest_unsent_d = d;
569 Add inexistent block to emerge queue.
571 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
573 //TODO: Get value from somewhere
574 // Allow only one block in emerge queue
575 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
576 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
578 //dstream<<"Adding block to emerge queue"<<std::endl;
580 // Add it to the emerge queue and trigger the thread
583 if(generate == false)
584 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
586 server->m_emerge_queue.addBlock(peer_id, p, flags);
587 server->m_emergethread.trigger();
595 Add block to send queue
598 PrioritySortedBlockTransfer q((float)d, p, peer_id);
602 num_blocks_selected += 1;
607 if(new_nearest_unsent_d != -1)
609 m_nearest_unsent_d = new_nearest_unsent_d;
613 void RemoteClient::SendObjectData(
616 core::map<v3s16, bool> &stepped_blocks
619 DSTACK(__FUNCTION_NAME);
621 // Can't send anything without knowing version
622 if(serialization_version == SER_FMT_VER_INVALID)
624 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
630 Send a TOCLIENT_OBJECTDATA packet.
634 u16 number of player positions
645 std::ostringstream os(std::ios_base::binary);
649 writeU16(buf, TOCLIENT_OBJECTDATA);
650 os.write((char*)buf, 2);
653 Get and write player data
656 // Get connected players
657 core::list<Player*> players = server->m_env.getPlayers(true);
659 // Write player count
660 u16 playercount = players.size();
661 writeU16(buf, playercount);
662 os.write((char*)buf, 2);
664 core::list<Player*>::Iterator i;
665 for(i = players.begin();
666 i != players.end(); i++)
670 v3f pf = player->getPosition();
671 v3f sf = player->getSpeed();
673 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
674 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
675 s32 pitch_i (player->getPitch() * 100);
676 s32 yaw_i (player->getYaw() * 100);
678 writeU16(buf, player->peer_id);
679 os.write((char*)buf, 2);
680 writeV3S32(buf, position_i);
681 os.write((char*)buf, 12);
682 writeV3S32(buf, speed_i);
683 os.write((char*)buf, 12);
684 writeS32(buf, pitch_i);
685 os.write((char*)buf, 4);
686 writeS32(buf, yaw_i);
687 os.write((char*)buf, 4);
691 Get and write object data
697 For making players to be able to build to their nearby
698 environment (building is not possible on blocks that are not
701 - Add blocks to emerge queue if they are not found
703 SUGGESTION: These could be ignored from the backside of the player
706 Player *player = server->m_env.getPlayer(peer_id);
710 v3f playerpos = player->getPosition();
711 v3f playerspeed = player->getSpeed();
713 v3s16 center_nodepos = floatToInt(playerpos, BS);
714 v3s16 center = getNodeBlockPos(center_nodepos);
716 s16 d_max = g_settings.getS16("active_object_range");
718 // Number of blocks whose objects were written to bos
721 std::ostringstream bos(std::ios_base::binary);
723 for(s16 d = 0; d <= d_max; d++)
725 core::list<v3s16> list;
726 getFacePositions(list, d);
728 core::list<v3s16>::Iterator li;
729 for(li=list.begin(); li!=list.end(); li++)
731 v3s16 p = *li + center;
734 Ignore blocks that haven't been sent to the client
737 if(m_blocks_sent.find(p) == NULL)
741 // Try stepping block and add it to a send queue
746 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
749 Step block if not in stepped_blocks and add to stepped_blocks.
751 if(stepped_blocks.find(p) == NULL)
753 block->stepObjects(dtime, true, server->getDayNightRatio());
754 stepped_blocks.insert(p, true);
755 block->setChangedFlag();
758 // Skip block if there are no objects
759 if(block->getObjectCount() == 0)
768 bos.write((char*)buf, 6);
771 block->serializeObjects(bos, serialization_version);
776 Stop collecting objects if data is already too big
778 // Sum of player and object data sizes
779 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
780 // break out if data too big
781 if(sum > MAX_OBJECTDATA_SIZE)
783 goto skip_subsequent;
787 catch(InvalidPositionException &e)
790 // Add it to the emerge queue and trigger the thread.
791 // Fetch the block only if it is on disk.
793 // Grab and increment counter
794 /*SharedPtr<JMutexAutoLock> lock
795 (m_num_blocks_in_emerge_queue.getLock());
796 m_num_blocks_in_emerge_queue.m_value++;*/
798 // Add to queue as an anonymous fetch from disk
799 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
800 server->m_emerge_queue.addBlock(0, p, flags);
801 server->m_emergethread.trigger();
809 writeU16(buf, blockcount);
810 os.write((char*)buf, 2);
812 // Write block objects
819 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
822 std::string s = os.str();
823 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
824 // Send as unreliable
825 server->m_con.Send(peer_id, 0, data, false);
828 void RemoteClient::GotBlock(v3s16 p)
830 if(m_blocks_sending.find(p) != NULL)
831 m_blocks_sending.remove(p);
834 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
835 " m_blocks_sending"<<std::endl;*/
836 m_excess_gotblocks++;
838 m_blocks_sent.insert(p, true);
841 void RemoteClient::SentBlock(v3s16 p)
843 if(m_blocks_sending.find(p) == NULL)
844 m_blocks_sending.insert(p, 0.0);
846 dstream<<"RemoteClient::SentBlock(): Sent block"
847 " already in m_blocks_sending"<<std::endl;
850 void RemoteClient::SetBlockNotSent(v3s16 p)
852 m_nearest_unsent_d = 0;
854 if(m_blocks_sending.find(p) != NULL)
855 m_blocks_sending.remove(p);
856 if(m_blocks_sent.find(p) != NULL)
857 m_blocks_sent.remove(p);
860 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
862 m_nearest_unsent_d = 0;
864 for(core::map<v3s16, MapBlock*>::Iterator
865 i = blocks.getIterator();
866 i.atEnd()==false; i++)
868 v3s16 p = i.getNode()->getKey();
870 if(m_blocks_sending.find(p) != NULL)
871 m_blocks_sending.remove(p);
872 if(m_blocks_sent.find(p) != NULL)
873 m_blocks_sent.remove(p);
881 PlayerInfo::PlayerInfo()
886 void PlayerInfo::PrintLine(std::ostream *s)
889 (*s)<<"\""<<name<<"\" ("
890 <<(position.X/10)<<","<<(position.Y/10)
891 <<","<<(position.Z/10)<<") ";
893 (*s)<<" avg_rtt="<<avg_rtt;
897 u32 PIChecksum(core::list<PlayerInfo> &l)
899 core::list<PlayerInfo>::Iterator i;
902 for(i=l.begin(); i!=l.end(); i++)
904 checksum += a * (i->id+1);
905 checksum ^= 0x435aafcd;
916 std::string mapsavedir
918 m_env(new ServerMap(mapsavedir), this),
919 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
921 m_emergethread(this),
924 m_time_of_day_send_timer(0),
926 m_mapsavedir(mapsavedir),
927 m_shutdown_requested(false),
928 m_ignore_map_edit_events(false),
929 m_ignore_map_edit_events_peer_id(0)
931 m_liquid_transform_timer = 0.0;
932 m_print_info_timer = 0.0;
933 m_objectdata_timer = 0.0;
934 m_emergethread_trigger_timer = 0.0;
935 m_savemap_timer = 0.0;
939 m_step_dtime_mutex.Init();
942 m_env.getMap().addEventReceiver(this);
945 m_env.deSerializePlayers(m_mapsavedir);
951 Send shutdown message
954 JMutexAutoLock conlock(m_con_mutex);
956 std::wstring line = L"*** Server shutting down";
959 Send the message to clients
961 for(core::map<u16, RemoteClient*>::Iterator
962 i = m_clients.getIterator();
963 i.atEnd() == false; i++)
965 // Get client and check that it is valid
966 RemoteClient *client = i.getNode()->getValue();
967 assert(client->peer_id == i.getNode()->getKey());
968 if(client->serialization_version == SER_FMT_VER_INVALID)
971 SendChatMessage(client->peer_id, line);
978 m_env.serializePlayers(m_mapsavedir);
989 JMutexAutoLock clientslock(m_con_mutex);
991 for(core::map<u16, RemoteClient*>::Iterator
992 i = m_clients.getIterator();
993 i.atEnd() == false; i++)
996 // NOTE: These are removed by env destructor
998 u16 peer_id = i.getNode()->getKey();
999 JMutexAutoLock envlock(m_env_mutex);
1000 m_env.removePlayer(peer_id);
1004 delete i.getNode()->getValue();
1009 void Server::start(unsigned short port)
1011 DSTACK(__FUNCTION_NAME);
1012 // Stop thread if already running
1015 // Initialize connection
1016 m_con.setTimeoutMs(30);
1020 m_thread.setRun(true);
1023 dout_server<<"Server: Started on port "<<port<<std::endl;
1028 DSTACK(__FUNCTION_NAME);
1030 // Stop threads (set run=false first so both start stopping)
1031 m_thread.setRun(false);
1032 m_emergethread.setRun(false);
1034 m_emergethread.stop();
1036 dout_server<<"Server: Threads stopped"<<std::endl;
1038 dout_server<<"Server: Saving players"<<std::endl;
1040 // FIXME: Apparently this does not do anything here
1041 //m_env.serializePlayers(m_mapsavedir);
1044 void Server::step(float dtime)
1046 DSTACK(__FUNCTION_NAME);
1051 JMutexAutoLock lock(m_step_dtime_mutex);
1052 m_step_dtime += dtime;
1056 void Server::AsyncRunStep()
1058 DSTACK(__FUNCTION_NAME);
1062 JMutexAutoLock lock1(m_step_dtime_mutex);
1063 dtime = m_step_dtime;
1066 // Send blocks to clients
1072 //dstream<<"Server steps "<<dtime<<std::endl;
1073 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1076 JMutexAutoLock lock1(m_step_dtime_mutex);
1077 m_step_dtime -= dtime;
1084 m_uptime.set(m_uptime.get() + dtime);
1088 Update m_time_of_day
1091 m_time_counter += dtime;
1092 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1093 u32 units = (u32)(m_time_counter*speed);
1094 m_time_counter -= (f32)units / speed;
1095 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1097 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1100 Send to clients at constant intervals
1103 m_time_of_day_send_timer -= dtime;
1104 if(m_time_of_day_send_timer < 0.0)
1106 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1108 //JMutexAutoLock envlock(m_env_mutex);
1109 JMutexAutoLock conlock(m_con_mutex);
1111 for(core::map<u16, RemoteClient*>::Iterator
1112 i = m_clients.getIterator();
1113 i.atEnd() == false; i++)
1115 RemoteClient *client = i.getNode()->getValue();
1116 //Player *player = m_env.getPlayer(client->peer_id);
1118 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1119 m_time_of_day.get());
1121 m_con.Send(client->peer_id, 0, data, true);
1127 // Process connection's timeouts
1128 JMutexAutoLock lock2(m_con_mutex);
1129 m_con.RunTimeouts(dtime);
1133 // This has to be called so that the client list gets synced
1134 // with the peer list of the connection
1135 handlePeerChanges();
1140 // This also runs Map's timers
1141 JMutexAutoLock lock(m_env_mutex);
1152 m_liquid_transform_timer += dtime;
1153 if(m_liquid_transform_timer >= 1.00)
1155 m_liquid_transform_timer -= 1.00;
1157 JMutexAutoLock lock(m_env_mutex);
1159 core::map<v3s16, MapBlock*> modified_blocks;
1160 m_env.getMap().transformLiquids(modified_blocks);
1165 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1166 ServerMap &map = ((ServerMap&)m_env.getMap());
1167 map.updateLighting(modified_blocks, lighting_modified_blocks);
1169 // Add blocks modified by lighting to modified_blocks
1170 for(core::map<v3s16, MapBlock*>::Iterator
1171 i = lighting_modified_blocks.getIterator();
1172 i.atEnd() == false; i++)
1174 MapBlock *block = i.getNode()->getValue();
1175 modified_blocks.insert(block->getPos(), block);
1179 Set the modified blocks unsent for all the clients
1182 JMutexAutoLock lock2(m_con_mutex);
1184 for(core::map<u16, RemoteClient*>::Iterator
1185 i = m_clients.getIterator();
1186 i.atEnd() == false; i++)
1188 RemoteClient *client = i.getNode()->getValue();
1190 if(modified_blocks.size() > 0)
1192 // Remove block from sent history
1193 client->SetBlocksNotSent(modified_blocks);
1198 // Periodically print some info
1200 float &counter = m_print_info_timer;
1206 JMutexAutoLock lock2(m_con_mutex);
1208 for(core::map<u16, RemoteClient*>::Iterator
1209 i = m_clients.getIterator();
1210 i.atEnd() == false; i++)
1212 //u16 peer_id = i.getNode()->getKey();
1213 RemoteClient *client = i.getNode()->getValue();
1214 Player *player = m_env.getPlayer(client->peer_id);
1215 std::cout<<player->getName()<<"\t";
1216 client->PrintInfo(std::cout);
1221 //if(g_settings.getBool("enable_experimental"))
1225 Check added and deleted active objects
1228 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1230 JMutexAutoLock envlock(m_env_mutex);
1231 JMutexAutoLock conlock(m_con_mutex);
1233 // Radius inside which objects are active
1236 for(core::map<u16, RemoteClient*>::Iterator
1237 i = m_clients.getIterator();
1238 i.atEnd() == false; i++)
1240 RemoteClient *client = i.getNode()->getValue();
1241 Player *player = m_env.getPlayer(client->peer_id);
1244 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1245 <<" has no associated player"<<std::endl;
1248 v3s16 pos = floatToInt(player->getPosition(), BS);
1250 core::map<u16, bool> removed_objects;
1251 core::map<u16, bool> added_objects;
1252 m_env.getRemovedActiveObjects(pos, radius,
1253 client->m_known_objects, removed_objects);
1254 m_env.getAddedActiveObjects(pos, radius,
1255 client->m_known_objects, added_objects);
1257 // Ignore if nothing happened
1258 if(removed_objects.size() == 0 && added_objects.size() == 0)
1260 //dstream<<"INFO: active objects: none changed"<<std::endl;
1264 std::string data_buffer;
1268 // Handle removed objects
1269 writeU16((u8*)buf, removed_objects.size());
1270 data_buffer.append(buf, 2);
1271 for(core::map<u16, bool>::Iterator
1272 i = removed_objects.getIterator();
1273 i.atEnd()==false; i++)
1276 u16 id = i.getNode()->getKey();
1277 ServerActiveObject* obj = m_env.getActiveObject(id);
1279 // Add to data buffer for sending
1280 writeU16((u8*)buf, i.getNode()->getKey());
1281 data_buffer.append(buf, 2);
1283 // Remove from known objects
1284 client->m_known_objects.remove(i.getNode()->getKey());
1286 if(obj && obj->m_known_by_count > 0)
1287 obj->m_known_by_count--;
1290 // Handle added objects
1291 writeU16((u8*)buf, added_objects.size());
1292 data_buffer.append(buf, 2);
1293 for(core::map<u16, bool>::Iterator
1294 i = added_objects.getIterator();
1295 i.atEnd()==false; i++)
1298 u16 id = i.getNode()->getKey();
1299 ServerActiveObject* obj = m_env.getActiveObject(id);
1302 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1304 dstream<<"WARNING: "<<__FUNCTION_NAME
1305 <<": NULL object"<<std::endl;
1307 type = obj->getType();
1309 // Add to data buffer for sending
1310 writeU16((u8*)buf, id);
1311 data_buffer.append(buf, 2);
1312 writeU8((u8*)buf, type);
1313 data_buffer.append(buf, 1);
1315 data_buffer.append(serializeLongString(
1316 obj->getClientInitializationData()));
1318 // Add to known objects
1319 client->m_known_objects.insert(i.getNode()->getKey(), false);
1322 obj->m_known_by_count++;
1326 SharedBuffer<u8> reply(2 + data_buffer.size());
1327 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1328 memcpy((char*)&reply[2], data_buffer.c_str(),
1329 data_buffer.size());
1331 m_con.Send(client->peer_id, 0, reply, true);
1333 dstream<<"INFO: Server: Sent object remove/add: "
1334 <<removed_objects.size()<<" removed, "
1335 <<added_objects.size()<<" added, "
1336 <<"packet size is "<<reply.getSize()<<std::endl;
1341 Collect a list of all the objects known by the clients
1342 and report it back to the environment.
1345 core::map<u16, bool> all_known_objects;
1347 for(core::map<u16, RemoteClient*>::Iterator
1348 i = m_clients.getIterator();
1349 i.atEnd() == false; i++)
1351 RemoteClient *client = i.getNode()->getValue();
1352 // Go through all known objects of client
1353 for(core::map<u16, bool>::Iterator
1354 i = client->m_known_objects.getIterator();
1355 i.atEnd()==false; i++)
1357 u16 id = i.getNode()->getKey();
1358 all_known_objects[id] = true;
1362 m_env.setKnownActiveObjects(whatever);
1368 Send object messages
1371 JMutexAutoLock envlock(m_env_mutex);
1372 JMutexAutoLock conlock(m_con_mutex);
1375 // Value = data sent by object
1376 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1378 // Get active object messages from environment
1381 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1385 core::list<ActiveObjectMessage>* message_list = NULL;
1386 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1387 n = buffered_messages.find(aom.id);
1390 message_list = new core::list<ActiveObjectMessage>;
1391 buffered_messages.insert(aom.id, message_list);
1395 message_list = n->getValue();
1397 message_list->push_back(aom);
1400 // Route data to every client
1401 for(core::map<u16, RemoteClient*>::Iterator
1402 i = m_clients.getIterator();
1403 i.atEnd()==false; i++)
1405 RemoteClient *client = i.getNode()->getValue();
1406 std::string reliable_data;
1407 std::string unreliable_data;
1408 // Go through all objects in message buffer
1409 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1410 j = buffered_messages.getIterator();
1411 j.atEnd()==false; j++)
1413 // If object is not known by client, skip it
1414 u16 id = j.getNode()->getKey();
1415 if(client->m_known_objects.find(id) == NULL)
1417 // Get message list of object
1418 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1419 // Go through every message
1420 for(core::list<ActiveObjectMessage>::Iterator
1421 k = list->begin(); k != list->end(); k++)
1423 // Compose the full new data with header
1424 ActiveObjectMessage aom = *k;
1425 std::string new_data;
1428 writeU16((u8*)&buf[0], aom.id);
1429 new_data.append(buf, 2);
1431 new_data += serializeString(aom.datastring);
1432 // Add data to buffer
1434 reliable_data += new_data;
1436 unreliable_data += new_data;
1440 reliable_data and unreliable_data are now ready.
1443 if(reliable_data.size() > 0)
1445 SharedBuffer<u8> reply(2 + reliable_data.size());
1446 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1447 memcpy((char*)&reply[2], reliable_data.c_str(),
1448 reliable_data.size());
1450 m_con.Send(client->peer_id, 0, reply, true);
1452 if(unreliable_data.size() > 0)
1454 SharedBuffer<u8> reply(2 + unreliable_data.size());
1455 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1456 memcpy((char*)&reply[2], unreliable_data.c_str(),
1457 unreliable_data.size());
1458 // Send as unreliable
1459 m_con.Send(client->peer_id, 0, reply, false);
1462 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1464 dstream<<"INFO: Server: Size of object message data: "
1465 <<"reliable: "<<reliable_data.size()
1466 <<", unreliable: "<<unreliable_data.size()
1471 // Clear buffered_messages
1472 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1473 i = buffered_messages.getIterator();
1474 i.atEnd()==false; i++)
1476 delete i.getNode()->getValue();
1480 } // enable_experimental
1483 Send queued-for-sending map edit events.
1486 while(m_unsent_map_edit_queue.size() != 0)
1488 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1490 if(event->type == MEET_ADDNODE)
1492 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1493 sendAddNode(event->p, event->n, event->already_known_by_peer);
1495 else if(event->type == MEET_REMOVENODE)
1497 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1498 sendRemoveNode(event->p, event->already_known_by_peer);
1500 else if(event->type == MEET_OTHER)
1502 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1507 dstream<<"WARNING: Server: Unknown MapEditEvent "
1508 <<((u32)event->type)<<std::endl;
1516 Send object positions
1517 TODO: Get rid of MapBlockObjects
1520 float &counter = m_objectdata_timer;
1522 if(counter >= g_settings.getFloat("objectdata_interval"))
1524 JMutexAutoLock lock1(m_env_mutex);
1525 JMutexAutoLock lock2(m_con_mutex);
1526 SendObjectData(counter);
1536 JMutexAutoLock envlock(m_env_mutex);
1537 JMutexAutoLock conlock(m_con_mutex);
1539 core::map<v3s16, MapBlock*> changed_blocks;
1540 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1542 for(core::map<v3s16, MapBlock*>::Iterator
1543 i = changed_blocks.getIterator();
1544 i.atEnd() == false; i++)
1546 MapBlock *block = i.getNode()->getValue();
1548 for(core::map<u16, RemoteClient*>::Iterator
1549 i = m_clients.getIterator();
1550 i.atEnd()==false; i++)
1552 RemoteClient *client = i.getNode()->getValue();
1553 client->SetBlockNotSent(block->getPos());
1559 Trigger emergethread (it somehow gets to a non-triggered but
1560 bysy state sometimes)
1563 float &counter = m_emergethread_trigger_timer;
1569 m_emergethread.trigger();
1575 float &counter = m_savemap_timer;
1577 if(counter >= g_settings.getFloat("server_map_save_interval"))
1581 JMutexAutoLock lock(m_env_mutex);
1583 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1585 // Save only changed parts
1586 m_env.getMap().save(true);
1588 // Delete unused sectors
1589 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1590 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1591 if(deleted_count > 0)
1593 dout_server<<"Server: Unloaded "<<deleted_count
1594 <<" sectors from memory"<<std::endl;
1598 m_env.serializePlayers(m_mapsavedir);
1604 void Server::Receive()
1606 DSTACK(__FUNCTION_NAME);
1607 u32 data_maxsize = 10000;
1608 Buffer<u8> data(data_maxsize);
1613 JMutexAutoLock conlock(m_con_mutex);
1614 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1617 // This has to be called so that the client list gets synced
1618 // with the peer list of the connection
1619 handlePeerChanges();
1621 ProcessData(*data, datasize, peer_id);
1623 catch(con::InvalidIncomingDataException &e)
1625 derr_server<<"Server::Receive(): "
1626 "InvalidIncomingDataException: what()="
1627 <<e.what()<<std::endl;
1629 catch(con::PeerNotFoundException &e)
1631 //NOTE: This is not needed anymore
1633 // The peer has been disconnected.
1634 // Find the associated player and remove it.
1636 /*JMutexAutoLock envlock(m_env_mutex);
1638 dout_server<<"ServerThread: peer_id="<<peer_id
1639 <<" has apparently closed connection. "
1640 <<"Removing player."<<std::endl;
1642 m_env.removePlayer(peer_id);*/
1646 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1648 DSTACK(__FUNCTION_NAME);
1649 // Environment is locked first.
1650 JMutexAutoLock envlock(m_env_mutex);
1651 JMutexAutoLock conlock(m_con_mutex);
1655 peer = m_con.GetPeer(peer_id);
1657 catch(con::PeerNotFoundException &e)
1659 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1660 <<peer_id<<" not found"<<std::endl;
1664 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1672 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1674 if(command == TOSERVER_INIT)
1676 // [0] u16 TOSERVER_INIT
1677 // [2] u8 SER_FMT_VER_HIGHEST
1678 // [3] u8[20] player_name
1683 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1684 <<peer->id<<std::endl;
1686 // First byte after command is maximum supported
1687 // serialization version
1688 u8 client_max = data[2];
1689 u8 our_max = SER_FMT_VER_HIGHEST;
1690 // Use the highest version supported by both
1691 u8 deployed = core::min_(client_max, our_max);
1692 // If it's lower than the lowest supported, give up.
1693 if(deployed < SER_FMT_VER_LOWEST)
1694 deployed = SER_FMT_VER_INVALID;
1696 //peer->serialization_version = deployed;
1697 getClient(peer->id)->pending_serialization_version = deployed;
1699 if(deployed == SER_FMT_VER_INVALID)
1701 derr_server<<DTIME<<"Server: Cannot negotiate "
1702 "serialization version with peer "
1703 <<peer_id<<std::endl;
1712 const u32 playername_size = 20;
1713 char playername[playername_size];
1714 for(u32 i=0; i<playername_size-1; i++)
1716 playername[i] = data[3+i];
1718 playername[playername_size-1] = 0;
1721 Player *player = emergePlayer(playername, "", peer_id);
1722 //Player *player = m_env.getPlayer(peer_id);
1725 // DEBUG: Test serialization
1726 std::ostringstream test_os;
1727 player->serialize(test_os);
1728 dstream<<"Player serialization test: \""<<test_os.str()
1730 std::istringstream test_is(test_os.str());
1731 player->deSerialize(test_is);
1734 // If failed, cancel
1737 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1738 <<": failed to emerge player"<<std::endl;
1743 // If a client is already connected to the player, cancel
1744 if(player->peer_id != 0)
1746 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1747 <<" tried to connect to "
1748 "an already connected player (peer_id="
1749 <<player->peer_id<<")"<<std::endl;
1752 // Set client of player
1753 player->peer_id = peer_id;
1756 // Check if player doesn't exist
1758 throw con::InvalidIncomingDataException
1759 ("Server::ProcessData(): INIT: Player doesn't exist");
1761 /*// update name if it was supplied
1762 if(datasize >= 20+3)
1765 player->updateName((const char*)&data[3]);
1768 // Now answer with a TOCLIENT_INIT
1770 SharedBuffer<u8> reply(2+1+6+8);
1771 writeU16(&reply[0], TOCLIENT_INIT);
1772 writeU8(&reply[2], deployed);
1773 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1774 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1777 m_con.Send(peer_id, 0, reply, true);
1781 if(command == TOSERVER_INIT2)
1783 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1784 <<peer->id<<std::endl;
1787 getClient(peer->id)->serialization_version
1788 = getClient(peer->id)->pending_serialization_version;
1791 Send some initialization data
1794 // Send player info to all players
1797 // Send inventory to player
1798 SendInventory(peer->id);
1802 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1803 m_time_of_day.get());
1804 m_con.Send(peer->id, 0, data, true);
1807 // Send information about server to player in chat
1808 SendChatMessage(peer_id, getStatusString());
1810 // Send information about joining in chat
1812 std::wstring name = L"unknown";
1813 Player *player = m_env.getPlayer(peer_id);
1815 name = narrow_to_wide(player->getName());
1817 std::wstring message;
1820 message += L" joined game";
1821 BroadcastChatMessage(message);
1827 if(peer_ser_ver == SER_FMT_VER_INVALID)
1829 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1830 " serialization format invalid or not initialized."
1831 " Skipping incoming command="<<command<<std::endl;
1835 Player *player = m_env.getPlayer(peer_id);
1838 derr_server<<"Server::ProcessData(): Cancelling: "
1839 "No player for peer_id="<<peer_id
1843 if(command == TOSERVER_PLAYERPOS)
1845 if(datasize < 2+12+12+4+4)
1849 v3s32 ps = readV3S32(&data[start+2]);
1850 v3s32 ss = readV3S32(&data[start+2+12]);
1851 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1852 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1853 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1854 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1855 pitch = wrapDegrees(pitch);
1856 yaw = wrapDegrees(yaw);
1857 player->setPosition(position);
1858 player->setSpeed(speed);
1859 player->setPitch(pitch);
1860 player->setYaw(yaw);
1862 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1863 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1864 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1866 else if(command == TOSERVER_GOTBLOCKS)
1879 u16 count = data[2];
1880 for(u16 i=0; i<count; i++)
1882 if((s16)datasize < 2+1+(i+1)*6)
1883 throw con::InvalidIncomingDataException
1884 ("GOTBLOCKS length is too short");
1885 v3s16 p = readV3S16(&data[2+1+i*6]);
1886 /*dstream<<"Server: GOTBLOCKS ("
1887 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1888 RemoteClient *client = getClient(peer_id);
1889 client->GotBlock(p);
1892 else if(command == TOSERVER_DELETEDBLOCKS)
1905 u16 count = data[2];
1906 for(u16 i=0; i<count; i++)
1908 if((s16)datasize < 2+1+(i+1)*6)
1909 throw con::InvalidIncomingDataException
1910 ("DELETEDBLOCKS length is too short");
1911 v3s16 p = readV3S16(&data[2+1+i*6]);
1912 /*dstream<<"Server: DELETEDBLOCKS ("
1913 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1914 RemoteClient *client = getClient(peer_id);
1915 client->SetBlockNotSent(p);
1918 else if(command == TOSERVER_CLICK_OBJECT)
1925 [2] u8 button (0=left, 1=right)
1930 u8 button = readU8(&data[2]);
1932 p.X = readS16(&data[3]);
1933 p.Y = readS16(&data[5]);
1934 p.Z = readS16(&data[7]);
1935 s16 id = readS16(&data[9]);
1936 //u16 item_i = readU16(&data[11]);
1938 MapBlock *block = NULL;
1941 block = m_env.getMap().getBlockNoCreate(p);
1943 catch(InvalidPositionException &e)
1945 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1949 MapBlockObject *obj = block->getObject(id);
1953 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1957 //TODO: Check that object is reasonably close
1962 InventoryList *ilist = player->inventory.getList("main");
1963 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1966 // Skip if inventory has no free space
1967 if(ilist->getUsedSlots() == ilist->getSize())
1969 dout_server<<"Player inventory has no free space"<<std::endl;
1974 Create the inventory item
1976 InventoryItem *item = NULL;
1977 // If it is an item-object, take the item from it
1978 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1980 item = ((ItemObject*)obj)->createInventoryItem();
1982 // Else create an item of the object
1985 item = new MapBlockObjectItem
1986 (obj->getInventoryString());
1989 // Add to inventory and send inventory
1990 ilist->addItem(item);
1991 SendInventory(player->peer_id);
1994 // Remove from block
1995 block->removeObject(id);
1998 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2006 [2] u8 button (0=left, 1=right)
2010 u8 button = readU8(&data[2]);
2011 u16 id = readS16(&data[3]);
2012 //u16 item_i = readU16(&data[11]);
2014 ServerActiveObject *obj = m_env.getActiveObject(id);
2018 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2023 //TODO: Check that object is reasonably close
2025 // Left click, pick object up (usually)
2028 InventoryList *ilist = player->inventory.getList("main");
2029 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2032 // Skip if inventory has no free space
2033 if(ilist->getUsedSlots() == ilist->getSize())
2035 dout_server<<"Player inventory has no free space"<<std::endl;
2039 // Skip if object has been removed
2044 Create the inventory item
2046 InventoryItem *item = obj->createPickedUpItem();
2050 // Add to inventory and send inventory
2051 ilist->addItem(item);
2052 SendInventory(player->peer_id);
2054 // Remove object from environment
2055 obj->m_removed = true;
2060 else if(command == TOSERVER_GROUND_ACTION)
2068 [3] v3s16 nodepos_undersurface
2069 [9] v3s16 nodepos_abovesurface
2074 2: stop digging (all parameters ignored)
2075 3: digging completed
2077 u8 action = readU8(&data[2]);
2079 p_under.X = readS16(&data[3]);
2080 p_under.Y = readS16(&data[5]);
2081 p_under.Z = readS16(&data[7]);
2083 p_over.X = readS16(&data[9]);
2084 p_over.Y = readS16(&data[11]);
2085 p_over.Z = readS16(&data[13]);
2086 u16 item_i = readU16(&data[15]);
2088 //TODO: Check that target is reasonably close
2096 NOTE: This can be used in the future to check if
2097 somebody is cheating, by checking the timing.
2104 else if(action == 2)
2107 RemoteClient *client = getClient(peer->id);
2108 JMutexAutoLock digmutex(client->m_dig_mutex);
2109 client->m_dig_tool_item = -1;
2114 3: Digging completed
2116 else if(action == 3)
2118 // Mandatory parameter; actually used for nothing
2119 core::map<v3s16, MapBlock*> modified_blocks;
2122 u8 mineral = MINERAL_NONE;
2124 bool cannot_remove_node = false;
2128 MapNode n = m_env.getMap().getNode(p_under);
2130 mineral = n.getMineral();
2131 // Get material at position
2133 // If not yet cancelled
2134 if(cannot_remove_node == false)
2136 // If it's not diggable, do nothing
2137 if(content_diggable(material) == false)
2139 derr_server<<"Server: Not finishing digging: "
2140 <<"Node not diggable"
2142 cannot_remove_node = true;
2145 // If not yet cancelled
2146 if(cannot_remove_node == false)
2148 // Get node metadata
2149 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2150 if(meta && meta->nodeRemovalDisabled() == true)
2152 derr_server<<"Server: Not finishing digging: "
2153 <<"Node metadata disables removal"
2155 cannot_remove_node = true;
2159 catch(InvalidPositionException &e)
2161 derr_server<<"Server: Not finishing digging: Node not found."
2162 <<" Adding block to emerge queue."
2164 m_emerge_queue.addBlock(peer_id,
2165 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2166 cannot_remove_node = true;
2170 If node can't be removed, set block to be re-sent to
2173 if(cannot_remove_node)
2175 derr_server<<"Server: Not finishing digging."<<std::endl;
2177 // Client probably has wrong data.
2178 // Set block not sent, so that client will get
2180 dstream<<"Client "<<peer_id<<" tried to dig "
2181 <<"node; but node cannot be removed."
2182 <<" setting MapBlock not sent."<<std::endl;
2183 RemoteClient *client = getClient(peer_id);
2184 v3s16 blockpos = getNodeBlockPos(p_under);
2185 client->SetBlockNotSent(blockpos);
2191 Send the removal to all other clients.
2192 - If other player is close, send REMOVENODE
2193 - Otherwise set blocks not sent
2195 core::list<u16> far_players;
2196 sendRemoveNode(p_under, peer_id, &far_players, 100);
2199 Update and send inventory
2202 if(g_settings.getBool("creative_mode") == false)
2207 InventoryList *mlist = player->inventory.getList("main");
2210 InventoryItem *item = mlist->getItem(item_i);
2211 if(item && (std::string)item->getName() == "ToolItem")
2213 ToolItem *titem = (ToolItem*)item;
2214 std::string toolname = titem->getToolName();
2216 // Get digging properties for material and tool
2217 DiggingProperties prop =
2218 getDiggingProperties(material, toolname);
2220 if(prop.diggable == false)
2222 derr_server<<"Server: WARNING: Player digged"
2223 <<" with impossible material + tool"
2224 <<" combination"<<std::endl;
2227 bool weared_out = titem->addWear(prop.wear);
2231 mlist->deleteItem(item_i);
2237 Add dug item to inventory
2240 InventoryItem *item = NULL;
2242 if(mineral != MINERAL_NONE)
2243 item = getDiggedMineralItem(mineral);
2248 std::string &dug_s = content_features(material).dug_item;
2251 std::istringstream is(dug_s, std::ios::binary);
2252 item = InventoryItem::deSerialize(is);
2258 // Add a item to inventory
2259 player->inventory.addItem("main", item);
2262 SendInventory(player->peer_id);
2268 (this takes some time so it is done after the quick stuff)
2270 m_ignore_map_edit_events = true;
2271 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2272 m_ignore_map_edit_events = false;
2275 Set blocks not sent to far players
2277 for(core::list<u16>::Iterator
2278 i = far_players.begin();
2279 i != far_players.end(); i++)
2282 RemoteClient *client = getClient(peer_id);
2285 client->SetBlocksNotSent(modified_blocks);
2292 else if(action == 1)
2295 InventoryList *ilist = player->inventory.getList("main");
2300 InventoryItem *item = ilist->getItem(item_i);
2302 // If there is no item, it is not possible to add it anywhere
2307 Handle material items
2309 if(std::string("MaterialItem") == item->getName())
2312 // Don't add a node if this is not a free space
2313 MapNode n2 = m_env.getMap().getNode(p_over);
2314 if(content_buildable_to(n2.d) == false)
2316 // Client probably has wrong data.
2317 // Set block not sent, so that client will get
2319 dstream<<"Client "<<peer_id<<" tried to place"
2320 <<" node in invalid position; setting"
2321 <<" MapBlock not sent."<<std::endl;
2322 RemoteClient *client = getClient(peer_id);
2323 v3s16 blockpos = getNodeBlockPos(p_over);
2324 client->SetBlockNotSent(blockpos);
2328 catch(InvalidPositionException &e)
2330 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2331 <<" Adding block to emerge queue."
2333 m_emerge_queue.addBlock(peer_id,
2334 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2338 // Reset build time counter
2339 getClient(peer->id)->m_time_from_building = 0.0;
2342 MaterialItem *mitem = (MaterialItem*)item;
2344 n.d = mitem->getMaterial();
2345 if(content_features(n.d).wall_mounted)
2346 n.dir = packDir(p_under - p_over);
2351 core::list<u16> far_players;
2352 sendAddNode(p_over, n, 0, &far_players, 100);
2357 InventoryList *ilist = player->inventory.getList("main");
2358 if(g_settings.getBool("creative_mode") == false && ilist)
2360 // Remove from inventory and send inventory
2361 if(mitem->getCount() == 1)
2362 ilist->deleteItem(item_i);
2366 SendInventory(peer_id);
2372 This takes some time so it is done after the quick stuff
2374 core::map<v3s16, MapBlock*> modified_blocks;
2375 m_ignore_map_edit_events = true;
2376 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2377 m_ignore_map_edit_events = false;
2380 Set blocks not sent to far players
2382 for(core::list<u16>::Iterator
2383 i = far_players.begin();
2384 i != far_players.end(); i++)
2387 RemoteClient *client = getClient(peer_id);
2390 client->SetBlocksNotSent(modified_blocks);
2394 Calculate special events
2397 /*if(n.d == CONTENT_MESE)
2400 for(s16 z=-1; z<=1; z++)
2401 for(s16 y=-1; y<=1; y++)
2402 for(s16 x=-1; x<=1; x++)
2409 Place other item (not a block)
2413 v3s16 blockpos = getNodeBlockPos(p_over);
2416 Check that the block is loaded so that the item
2417 can properly be added to the static list too
2419 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2422 derr_server<<"Error while placing object: "
2423 "block not found"<<std::endl;
2427 dout_server<<"Placing a miscellaneous item on map"
2430 // Calculate a position for it
2431 v3f pos = intToFloat(p_over, BS);
2433 pos.Y -= BS*0.25; // let it drop a bit
2435 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2436 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2441 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2445 derr_server<<"WARNING: item resulted in NULL object, "
2446 <<"not placing onto map"
2451 // Add the object to the environment
2452 m_env.addActiveObject(obj);
2454 dout_server<<"Placed object"<<std::endl;
2456 // If item has count<=1, delete it
2457 if(item->getCount() <= 1)
2459 InventoryList *ilist = player->inventory.getList("main");
2460 if(g_settings.getBool("creative_mode") == false && ilist)
2462 // Remove from inventory and send inventory
2463 ilist->deleteItem(item_i);
2465 SendInventory(peer_id);
2468 // Else decrement it
2473 SendInventory(peer_id);
2481 Catch invalid actions
2485 derr_server<<"WARNING: Server: Invalid action "
2486 <<action<<std::endl;
2490 else if(command == TOSERVER_RELEASE)
2499 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2502 else if(command == TOSERVER_SIGNTEXT)
2511 std::string datastring((char*)&data[2], datasize-2);
2512 std::istringstream is(datastring, std::ios_base::binary);
2515 is.read((char*)buf, 6);
2516 v3s16 blockpos = readV3S16(buf);
2517 is.read((char*)buf, 2);
2518 s16 id = readS16(buf);
2519 is.read((char*)buf, 2);
2520 u16 textlen = readU16(buf);
2522 for(u16 i=0; i<textlen; i++)
2524 is.read((char*)buf, 1);
2525 text += (char)buf[0];
2528 MapBlock *block = NULL;
2531 block = m_env.getMap().getBlockNoCreate(blockpos);
2533 catch(InvalidPositionException &e)
2535 derr_server<<"Error while setting sign text: "
2536 "block not found"<<std::endl;
2540 MapBlockObject *obj = block->getObject(id);
2543 derr_server<<"Error while setting sign text: "
2544 "object not found"<<std::endl;
2548 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2550 derr_server<<"Error while setting sign text: "
2551 "object is not a sign"<<std::endl;
2555 ((SignObject*)obj)->setText(text);
2557 obj->getBlock()->setChangedFlag();
2559 else if(command == TOSERVER_SIGNNODETEXT)
2567 std::string datastring((char*)&data[2], datasize-2);
2568 std::istringstream is(datastring, std::ios_base::binary);
2571 is.read((char*)buf, 6);
2572 v3s16 p = readV3S16(buf);
2573 is.read((char*)buf, 2);
2574 u16 textlen = readU16(buf);
2576 for(u16 i=0; i<textlen; i++)
2578 is.read((char*)buf, 1);
2579 text += (char)buf[0];
2582 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2585 if(meta->typeId() != CONTENT_SIGN_WALL)
2587 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2588 signmeta->setText(text);
2590 v3s16 blockpos = getNodeBlockPos(p);
2591 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2594 block->setChangedFlag();
2597 for(core::map<u16, RemoteClient*>::Iterator
2598 i = m_clients.getIterator();
2599 i.atEnd()==false; i++)
2601 RemoteClient *client = i.getNode()->getValue();
2602 client->SetBlockNotSent(blockpos);
2605 else if(command == TOSERVER_INVENTORY_ACTION)
2607 /*// Ignore inventory changes if in creative mode
2608 if(g_settings.getBool("creative_mode") == true)
2610 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2614 // Strip command and create a stream
2615 std::string datastring((char*)&data[2], datasize-2);
2616 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2617 std::istringstream is(datastring, std::ios_base::binary);
2619 InventoryAction *a = InventoryAction::deSerialize(is);
2624 c.current_player = player;
2627 Handle craftresult specially if not in creative mode
2629 bool disable_action = false;
2630 if(a->getType() == IACTION_MOVE
2631 && g_settings.getBool("creative_mode") == false)
2633 IMoveAction *ma = (IMoveAction*)a;
2634 if(ma->to_inv == "current_player" &&
2635 ma->from_inv == "current_player")
2637 // Don't allow moving anything to craftresult
2638 if(ma->to_list == "craftresult")
2641 disable_action = true;
2643 // When something is removed from craftresult
2644 if(ma->from_list == "craftresult")
2646 disable_action = true;
2647 // Remove stuff from craft
2648 InventoryList *clist = player->inventory.getList("craft");
2651 u16 count = ma->count;
2654 clist->decrementMaterials(count);
2657 // Feed action to player inventory
2658 //a->apply(&player->inventory);
2662 // If something appeared in craftresult, throw it
2664 InventoryList *rlist = player->inventory.getList("craftresult");
2665 InventoryList *mlist = player->inventory.getList("main");
2666 if(rlist && mlist && rlist->getUsedSlots() == 1)
2668 InventoryItem *item1 = rlist->changeItem(0, NULL);
2669 mlist->addItem(item1);
2675 if(disable_action == false)
2677 // Feed action to player inventory
2678 //a->apply(&player->inventory);
2686 SendInventory(player->peer_id);
2691 dstream<<"TOSERVER_INVENTORY_ACTION: "
2692 <<"InventoryAction::deSerialize() returned NULL"
2696 else if(command == TOSERVER_CHAT_MESSAGE)
2704 std::string datastring((char*)&data[2], datasize-2);
2705 std::istringstream is(datastring, std::ios_base::binary);
2708 is.read((char*)buf, 2);
2709 u16 len = readU16(buf);
2711 std::wstring message;
2712 for(u16 i=0; i<len; i++)
2714 is.read((char*)buf, 2);
2715 message += (wchar_t)readU16(buf);
2718 // Get player name of this client
2719 std::wstring name = narrow_to_wide(player->getName());
2721 // Line to send to players
2723 // Whether to send to the player that sent the line
2724 bool send_to_sender = false;
2725 // Whether to send to other players
2726 bool send_to_others = false;
2729 std::wstring commandprefix = L"/#";
2730 if(message.substr(0, commandprefix.size()) == commandprefix)
2732 line += L"Server: ";
2734 message = message.substr(commandprefix.size());
2735 // Get player name as narrow string
2736 std::string name_s = player->getName();
2737 // Convert message to narrow string
2738 std::string message_s = wide_to_narrow(message);
2739 // Operator is the single name defined in config.
2740 std::string operator_name = g_settings.get("name");
2741 bool is_operator = (operator_name != "" &&
2742 wide_to_narrow(name) == operator_name);
2743 bool valid_command = false;
2744 if(message_s == "help")
2746 line += L"-!- Available commands: ";
2750 line += L"shutdown setting ";
2755 send_to_sender = true;
2756 valid_command = true;
2758 else if(message_s == "status")
2760 line = getStatusString();
2761 send_to_sender = true;
2762 valid_command = true;
2764 else if(is_operator)
2766 if(message_s == "shutdown")
2768 dstream<<DTIME<<" Server: Operator requested shutdown."
2770 m_shutdown_requested.set(true);
2772 line += L"*** Server shutting down (operator request)";
2773 send_to_sender = true;
2774 valid_command = true;
2776 else if(message_s.substr(0,8) == "setting ")
2778 std::string confline = message_s.substr(8);
2779 g_settings.parseConfigLine(confline);
2780 line += L"-!- Setting changed.";
2781 send_to_sender = true;
2782 valid_command = true;
2786 if(valid_command == false)
2788 line += L"-!- Invalid command: " + message;
2789 send_to_sender = true;
2800 send_to_others = true;
2805 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2808 Send the message to clients
2810 for(core::map<u16, RemoteClient*>::Iterator
2811 i = m_clients.getIterator();
2812 i.atEnd() == false; i++)
2814 // Get client and check that it is valid
2815 RemoteClient *client = i.getNode()->getValue();
2816 assert(client->peer_id == i.getNode()->getKey());
2817 if(client->serialization_version == SER_FMT_VER_INVALID)
2821 bool sender_selected = (peer_id == client->peer_id);
2822 if(sender_selected == true && send_to_sender == false)
2824 if(sender_selected == false && send_to_others == false)
2827 SendChatMessage(client->peer_id, line);
2833 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2834 "unknown command "<<command<<std::endl;
2838 catch(SendFailedException &e)
2840 derr_server<<"Server::ProcessData(): SendFailedException: "
2846 void Server::onMapEditEvent(MapEditEvent *event)
2848 dstream<<"Server::onMapEditEvent()"<<std::endl;
2849 if(m_ignore_map_edit_events)
2851 MapEditEvent *e = event->clone();
2852 m_unsent_map_edit_queue.push_back(e);
2855 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2857 if(id == "current_player")
2859 assert(c->current_player);
2860 return &(c->current_player->inventory);
2864 std::string id0 = fn.next(":");
2866 if(id0 == "nodemeta")
2869 p.X = stoi(fn.next(","));
2870 p.Y = stoi(fn.next(","));
2871 p.Z = stoi(fn.next(","));
2872 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2874 return meta->getInventory();
2875 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2876 <<"no metadata found"<<std::endl;
2880 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2883 void Server::inventoryModified(InventoryContext *c, std::string id)
2885 if(id == "current_player")
2887 assert(c->current_player);
2889 SendInventory(c->current_player->peer_id);
2894 std::string id0 = fn.next(":");
2896 if(id0 == "nodemeta")
2899 p.X = stoi(fn.next(","));
2900 p.Y = stoi(fn.next(","));
2901 p.Z = stoi(fn.next(","));
2902 v3s16 blockpos = getNodeBlockPos(p);
2904 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2906 meta->inventoryModified();
2908 for(core::map<u16, RemoteClient*>::Iterator
2909 i = m_clients.getIterator();
2910 i.atEnd()==false; i++)
2912 RemoteClient *client = i.getNode()->getValue();
2913 client->SetBlockNotSent(blockpos);
2919 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2922 core::list<PlayerInfo> Server::getPlayerInfo()
2924 DSTACK(__FUNCTION_NAME);
2925 JMutexAutoLock envlock(m_env_mutex);
2926 JMutexAutoLock conlock(m_con_mutex);
2928 core::list<PlayerInfo> list;
2930 core::list<Player*> players = m_env.getPlayers();
2932 core::list<Player*>::Iterator i;
2933 for(i = players.begin();
2934 i != players.end(); i++)
2938 Player *player = *i;
2941 con::Peer *peer = m_con.GetPeer(player->peer_id);
2942 // Copy info from peer to info struct
2944 info.address = peer->address;
2945 info.avg_rtt = peer->avg_rtt;
2947 catch(con::PeerNotFoundException &e)
2949 // Set dummy peer info
2951 info.address = Address(0,0,0,0,0);
2955 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2956 info.position = player->getPosition();
2958 list.push_back(info);
2965 void Server::peerAdded(con::Peer *peer)
2967 DSTACK(__FUNCTION_NAME);
2968 dout_server<<"Server::peerAdded(): peer->id="
2969 <<peer->id<<std::endl;
2972 c.type = PEER_ADDED;
2973 c.peer_id = peer->id;
2975 m_peer_change_queue.push_back(c);
2978 void Server::deletingPeer(con::Peer *peer, bool timeout)
2980 DSTACK(__FUNCTION_NAME);
2981 dout_server<<"Server::deletingPeer(): peer->id="
2982 <<peer->id<<", timeout="<<timeout<<std::endl;
2985 c.type = PEER_REMOVED;
2986 c.peer_id = peer->id;
2987 c.timeout = timeout;
2988 m_peer_change_queue.push_back(c);
2991 void Server::SendObjectData(float dtime)
2993 DSTACK(__FUNCTION_NAME);
2995 core::map<v3s16, bool> stepped_blocks;
2997 for(core::map<u16, RemoteClient*>::Iterator
2998 i = m_clients.getIterator();
2999 i.atEnd() == false; i++)
3001 u16 peer_id = i.getNode()->getKey();
3002 RemoteClient *client = i.getNode()->getValue();
3003 assert(client->peer_id == peer_id);
3005 if(client->serialization_version == SER_FMT_VER_INVALID)
3008 client->SendObjectData(this, dtime, stepped_blocks);
3012 void Server::SendPlayerInfos()
3014 DSTACK(__FUNCTION_NAME);
3016 //JMutexAutoLock envlock(m_env_mutex);
3018 // Get connected players
3019 core::list<Player*> players = m_env.getPlayers(true);
3021 u32 player_count = players.getSize();
3022 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3024 SharedBuffer<u8> data(datasize);
3025 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3028 core::list<Player*>::Iterator i;
3029 for(i = players.begin();
3030 i != players.end(); i++)
3032 Player *player = *i;
3034 /*dstream<<"Server sending player info for player with "
3035 "peer_id="<<player->peer_id<<std::endl;*/
3037 writeU16(&data[start], player->peer_id);
3038 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3039 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3040 start += 2+PLAYERNAME_SIZE;
3043 //JMutexAutoLock conlock(m_con_mutex);
3046 m_con.SendToAll(0, data, true);
3049 void Server::SendInventory(u16 peer_id)
3051 DSTACK(__FUNCTION_NAME);
3053 Player* player = m_env.getPlayer(peer_id);
3056 Calculate crafting stuff
3058 if(g_settings.getBool("creative_mode") == false)
3060 InventoryList *clist = player->inventory.getList("craft");
3061 InventoryList *rlist = player->inventory.getList("craftresult");
3064 rlist->clearItems();
3068 InventoryItem *items[9];
3069 for(u16 i=0; i<9; i++)
3071 items[i] = clist->getItem(i);
3080 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3081 if(checkItemCombination(items, specs))
3083 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3092 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3093 if(checkItemCombination(items, specs))
3095 rlist->addItem(new CraftItem("Stick", 4));
3104 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3105 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3106 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3107 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3108 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3109 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3110 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3111 if(checkItemCombination(items, specs))
3113 //rlist->addItem(new MapBlockObjectItem("Sign"));
3114 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3123 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3124 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3125 if(checkItemCombination(items, specs))
3127 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3136 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3137 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3138 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3139 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3140 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3141 if(checkItemCombination(items, specs))
3143 rlist->addItem(new ToolItem("WPick", 0));
3152 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3153 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3154 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3155 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3156 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3157 if(checkItemCombination(items, specs))
3159 rlist->addItem(new ToolItem("STPick", 0));
3168 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3169 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3170 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3171 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3172 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3173 if(checkItemCombination(items, specs))
3175 rlist->addItem(new ToolItem("SteelPick", 0));
3184 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3185 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3186 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3187 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3188 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3189 if(checkItemCombination(items, specs))
3191 rlist->addItem(new ToolItem("MesePick", 0));
3200 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3201 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3202 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3203 if(checkItemCombination(items, specs))
3205 rlist->addItem(new ToolItem("WShovel", 0));
3214 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3215 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3216 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3217 if(checkItemCombination(items, specs))
3219 rlist->addItem(new ToolItem("STShovel", 0));
3228 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3229 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3230 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3231 if(checkItemCombination(items, specs))
3233 rlist->addItem(new ToolItem("SteelShovel", 0));
3242 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3243 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3244 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3245 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3246 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3247 if(checkItemCombination(items, specs))
3249 rlist->addItem(new ToolItem("WAxe", 0));
3258 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3259 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3260 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3261 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3262 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3263 if(checkItemCombination(items, specs))
3265 rlist->addItem(new ToolItem("STAxe", 0));
3274 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3275 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3276 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3277 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3278 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3279 if(checkItemCombination(items, specs))
3281 rlist->addItem(new ToolItem("SteelAxe", 0));
3290 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3291 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3292 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3293 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3294 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3295 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3296 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3297 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3298 if(checkItemCombination(items, specs))
3300 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3309 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3310 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3311 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3312 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3313 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3314 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3315 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3316 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3317 if(checkItemCombination(items, specs))
3319 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3328 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3329 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3330 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3331 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3332 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3333 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3334 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3335 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3336 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3337 if(checkItemCombination(items, specs))
3339 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3345 } // if creative_mode == false
3351 std::ostringstream os;
3352 //os.imbue(std::locale("C"));
3354 player->inventory.serialize(os);
3356 std::string s = os.str();
3358 SharedBuffer<u8> data(s.size()+2);
3359 writeU16(&data[0], TOCLIENT_INVENTORY);
3360 memcpy(&data[2], s.c_str(), s.size());
3363 m_con.Send(peer_id, 0, data, true);
3366 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3368 DSTACK(__FUNCTION_NAME);
3370 std::ostringstream os(std::ios_base::binary);
3374 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3375 os.write((char*)buf, 2);
3378 writeU16(buf, message.size());
3379 os.write((char*)buf, 2);
3382 for(u32 i=0; i<message.size(); i++)
3386 os.write((char*)buf, 2);
3390 std::string s = os.str();
3391 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3393 m_con.Send(peer_id, 0, data, true);
3396 void Server::BroadcastChatMessage(const std::wstring &message)
3398 for(core::map<u16, RemoteClient*>::Iterator
3399 i = m_clients.getIterator();
3400 i.atEnd() == false; i++)
3402 // Get client and check that it is valid
3403 RemoteClient *client = i.getNode()->getValue();
3404 assert(client->peer_id == i.getNode()->getKey());
3405 if(client->serialization_version == SER_FMT_VER_INVALID)
3408 SendChatMessage(client->peer_id, message);
3412 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3413 core::list<u16> *far_players, float far_d_nodes)
3415 float maxd = far_d_nodes*BS;
3416 v3f p_f = intToFloat(p, BS);
3420 SharedBuffer<u8> reply(replysize);
3421 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3422 writeS16(&reply[2], p.X);
3423 writeS16(&reply[4], p.Y);
3424 writeS16(&reply[6], p.Z);
3426 for(core::map<u16, RemoteClient*>::Iterator
3427 i = m_clients.getIterator();
3428 i.atEnd() == false; i++)
3430 // Get client and check that it is valid
3431 RemoteClient *client = i.getNode()->getValue();
3432 assert(client->peer_id == i.getNode()->getKey());
3433 if(client->serialization_version == SER_FMT_VER_INVALID)
3436 // Don't send if it's the same one
3437 if(client->peer_id == ignore_id)
3443 Player *player = m_env.getPlayer(client->peer_id);
3446 // If player is far away, only set modified blocks not sent
3447 v3f player_pos = player->getPosition();
3448 if(player_pos.getDistanceFrom(p_f) > maxd)
3450 far_players->push_back(client->peer_id);
3457 m_con.Send(client->peer_id, 0, reply, true);
3461 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3462 core::list<u16> *far_players, float far_d_nodes)
3464 float maxd = far_d_nodes*BS;
3465 v3f p_f = intToFloat(p, BS);
3467 for(core::map<u16, RemoteClient*>::Iterator
3468 i = m_clients.getIterator();
3469 i.atEnd() == false; i++)
3471 // Get client and check that it is valid
3472 RemoteClient *client = i.getNode()->getValue();
3473 assert(client->peer_id == i.getNode()->getKey());
3474 if(client->serialization_version == SER_FMT_VER_INVALID)
3477 // Don't send if it's the same one
3478 if(client->peer_id == ignore_id)
3484 Player *player = m_env.getPlayer(client->peer_id);
3487 // If player is far away, only set modified blocks not sent
3488 v3f player_pos = player->getPosition();
3489 if(player_pos.getDistanceFrom(p_f) > maxd)
3491 far_players->push_back(client->peer_id);
3498 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3499 SharedBuffer<u8> reply(replysize);
3500 writeU16(&reply[0], TOCLIENT_ADDNODE);
3501 writeS16(&reply[2], p.X);
3502 writeS16(&reply[4], p.Y);
3503 writeS16(&reply[6], p.Z);
3504 n.serialize(&reply[8], client->serialization_version);
3507 m_con.Send(client->peer_id, 0, reply, true);
3511 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3513 DSTACK(__FUNCTION_NAME);
3515 Create a packet with the block in the right format
3518 std::ostringstream os(std::ios_base::binary);
3519 block->serialize(os, ver);
3520 std::string s = os.str();
3521 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3523 u32 replysize = 8 + blockdata.getSize();
3524 SharedBuffer<u8> reply(replysize);
3525 v3s16 p = block->getPos();
3526 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3527 writeS16(&reply[2], p.X);
3528 writeS16(&reply[4], p.Y);
3529 writeS16(&reply[6], p.Z);
3530 memcpy(&reply[8], *blockdata, blockdata.getSize());
3532 /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3533 <<": \tpacket size: "<<replysize<<std::endl;*/
3538 m_con.Send(peer_id, 1, reply, true);
3541 void Server::SendBlocks(float dtime)
3543 DSTACK(__FUNCTION_NAME);
3545 JMutexAutoLock envlock(m_env_mutex);
3546 JMutexAutoLock conlock(m_con_mutex);
3548 //TimeTaker timer("Server::SendBlocks");
3550 core::array<PrioritySortedBlockTransfer> queue;
3552 s32 total_sending = 0;
3554 for(core::map<u16, RemoteClient*>::Iterator
3555 i = m_clients.getIterator();
3556 i.atEnd() == false; i++)
3558 RemoteClient *client = i.getNode()->getValue();
3559 assert(client->peer_id == i.getNode()->getKey());
3561 total_sending += client->SendingCount();
3563 if(client->serialization_version == SER_FMT_VER_INVALID)
3566 client->GetNextBlocks(this, dtime, queue);
3570 // Lowest priority number comes first.
3571 // Lowest is most important.
3574 for(u32 i=0; i<queue.size(); i++)
3576 //TODO: Calculate limit dynamically
3577 if(total_sending >= g_settings.getS32
3578 ("max_simultaneous_block_sends_server_total"))
3581 PrioritySortedBlockTransfer q = queue[i];
3583 MapBlock *block = NULL;
3586 block = m_env.getMap().getBlockNoCreate(q.pos);
3588 catch(InvalidPositionException &e)
3593 RemoteClient *client = getClient(q.peer_id);
3595 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3597 client->SentBlock(q.pos);
3604 RemoteClient* Server::getClient(u16 peer_id)
3606 DSTACK(__FUNCTION_NAME);
3607 //JMutexAutoLock lock(m_con_mutex);
3608 core::map<u16, RemoteClient*>::Node *n;
3609 n = m_clients.find(peer_id);
3610 // A client should exist for all peers
3612 return n->getValue();
3615 std::wstring Server::getStatusString()
3617 std::wostringstream os(std::ios_base::binary);
3620 os<<L"uptime="<<m_uptime.get();
3621 // Information about clients
3623 for(core::map<u16, RemoteClient*>::Iterator
3624 i = m_clients.getIterator();
3625 i.atEnd() == false; i++)
3627 // Get client and check that it is valid
3628 RemoteClient *client = i.getNode()->getValue();
3629 assert(client->peer_id == i.getNode()->getKey());
3630 if(client->serialization_version == SER_FMT_VER_INVALID)
3633 Player *player = m_env.getPlayer(client->peer_id);
3634 // Get name of player
3635 std::wstring name = L"unknown";
3637 name = narrow_to_wide(player->getName());
3638 // Add name to information string
3642 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3643 os<<" WARNING: Map saving is disabled."<<std::endl;
3648 void setCreativeInventory(Player *player)
3650 player->resetInventory();
3652 // Give some good picks
3654 InventoryItem *item = new ToolItem("STPick", 0);
3655 void* r = player->inventory.addItem("main", item);
3659 InventoryItem *item = new ToolItem("MesePick", 0);
3660 void* r = player->inventory.addItem("main", item);
3668 // CONTENT_IGNORE-terminated list
3669 u8 material_items[] = {
3678 CONTENT_WATERSOURCE,
3686 u8 *mip = material_items;
3687 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3689 if(*mip == CONTENT_IGNORE)
3692 InventoryItem *item = new MaterialItem(*mip, 1);
3693 player->inventory.addItem("main", item);
3699 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3702 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3703 player->inventory.addItem("main", item);
3706 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3708 // Skip some materials
3709 if(i == CONTENT_WATER || i == CONTENT_TORCH
3710 || i == CONTENT_COALSTONE)
3713 InventoryItem *item = new MaterialItem(i, 1);
3714 player->inventory.addItem("main", item);
3720 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3721 void* r = player->inventory.addItem("main", item);
3726 Player *Server::emergePlayer(const char *name, const char *password,
3730 Try to get an existing player
3732 Player *player = m_env.getPlayer(name);
3735 // If player is already connected, cancel
3736 if(player->peer_id != 0)
3738 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3743 player->peer_id = peer_id;
3745 // Reset inventory to creative if in creative mode
3746 if(g_settings.getBool("creative_mode"))
3748 setCreativeInventory(player);
3755 If player with the wanted peer_id already exists, cancel.
3757 if(m_env.getPlayer(peer_id) != NULL)
3759 dstream<<"emergePlayer(): Player with wrong name but same"
3760 " peer_id already exists"<<std::endl;
3768 player = new ServerRemotePlayer();
3769 //player->peer_id = c.peer_id;
3770 //player->peer_id = PEER_ID_INEXISTENT;
3771 player->peer_id = peer_id;
3772 player->updateName(name);
3778 dstream<<"Server: Finding spawn place for player \""
3779 <<player->getName()<<"\""<<std::endl;
3783 player->setPosition(intToFloat(v3s16(
3790 s16 groundheight = 0;
3792 // Try to find a good place a few times
3793 for(s32 i=0; i<1000; i++)
3796 // We're going to try to throw the player to this position
3797 nodepos = v2s16(-range + (myrand()%(range*2)),
3798 -range + (myrand()%(range*2)));
3799 v2s16 sectorpos = getNodeSectorPos(nodepos);
3800 // Get sector (NOTE: Don't get because it's slow)
3801 //m_env.getMap().emergeSector(sectorpos);
3802 // Get ground height at point (fallbacks to heightmap function)
3803 groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3804 // Don't go underwater
3805 if(groundheight < WATER_LEVEL)
3807 //dstream<<"-> Underwater"<<std::endl;
3810 // Don't go to high places
3811 if(groundheight > WATER_LEVEL + 4)
3813 //dstream<<"-> Underwater"<<std::endl;
3817 // Found a good place
3818 dstream<<"Searched through "<<i<<" places."<<std::endl;
3823 // If no suitable place was not found, go above water at least.
3824 if(groundheight < WATER_LEVEL)
3825 groundheight = WATER_LEVEL;
3827 player->setPosition(intToFloat(v3s16(
3829 groundheight + 5, // Accomodate mud
3835 Add player to environment
3838 m_env.addPlayer(player);
3841 Add stuff to inventory
3844 if(g_settings.getBool("creative_mode"))
3846 setCreativeInventory(player);
3851 InventoryItem *item = new ToolItem("WPick", 32000);
3852 void* r = player->inventory.addItem("main", item);
3856 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3857 void* r = player->inventory.addItem("main", item);
3861 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3862 void* r = player->inventory.addItem("main", item);
3866 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3867 void* r = player->inventory.addItem("main", item);
3871 InventoryItem *item = new CraftItem("Stick", 4);
3872 void* r = player->inventory.addItem("main", item);
3876 InventoryItem *item = new ToolItem("WPick", 32000);
3877 void* r = player->inventory.addItem("main", item);
3881 InventoryItem *item = new ToolItem("STPick", 32000);
3882 void* r = player->inventory.addItem("main", item);
3885 /*// Give some lights
3887 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3888 bool r = player->inventory.addItem("main", item);
3892 for(u16 i=0; i<4; i++)
3894 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3895 bool r = player->inventory.addItem("main", item);
3898 /*// Give some other stuff
3900 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3901 bool r = player->inventory.addItem("main", item);
3908 } // create new player
3911 void Server::handlePeerChange(PeerChange &c)
3913 JMutexAutoLock envlock(m_env_mutex);
3914 JMutexAutoLock conlock(m_con_mutex);
3916 if(c.type == PEER_ADDED)
3923 core::map<u16, RemoteClient*>::Node *n;
3924 n = m_clients.find(c.peer_id);
3925 // The client shouldn't already exist
3929 RemoteClient *client = new RemoteClient();
3930 client->peer_id = c.peer_id;
3931 m_clients.insert(client->peer_id, client);
3934 else if(c.type == PEER_REMOVED)
3941 core::map<u16, RemoteClient*>::Node *n;
3942 n = m_clients.find(c.peer_id);
3943 // The client should exist
3946 // Collect information about leaving in chat
3947 std::wstring message;
3949 std::wstring name = L"unknown";
3950 Player *player = m_env.getPlayer(c.peer_id);
3952 name = narrow_to_wide(player->getName());
3956 message += L" left game";
3958 message += L" (timed out)";
3963 m_env.removePlayer(c.peer_id);
3966 // Set player client disconnected
3968 Player *player = m_env.getPlayer(c.peer_id);
3970 player->peer_id = 0;
3974 delete m_clients[c.peer_id];
3975 m_clients.remove(c.peer_id);
3977 // Send player info to all remaining clients
3980 // Send leave chat message to all remaining clients
3981 BroadcastChatMessage(message);
3990 void Server::handlePeerChanges()
3992 while(m_peer_change_queue.size() > 0)
3994 PeerChange c = m_peer_change_queue.pop_front();
3996 dout_server<<"Server: Handling peer change: "
3997 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4000 handlePeerChange(c);
4004 void dedicated_server_loop(Server &server, bool &kill)
4006 DSTACK(__FUNCTION_NAME);
4008 std::cout<<DTIME<<std::endl;
4009 std::cout<<"========================"<<std::endl;
4010 std::cout<<"Running dedicated server"<<std::endl;
4011 std::cout<<"========================"<<std::endl;
4012 std::cout<<std::endl;
4016 // This is kind of a hack but can be done like this
4017 // because server.step() is very light
4021 if(server.getShutdownRequested() || kill)
4023 std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4027 static int counter = 0;
4033 core::list<PlayerInfo> list = server.getPlayerInfo();
4034 core::list<PlayerInfo>::Iterator i;
4035 static u32 sum_old = 0;
4036 u32 sum = PIChecksum(list);
4039 std::cout<<DTIME<<"Player info:"<<std::endl;
4040 for(i=list.begin(); i!=list.end(); i++)
4042 i->PrintLine(&std::cout);