]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
Fixed error reported by cppcheck:
[minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31
32 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
33
34 void * ServerThread::Thread()
35 {
36         ThreadStarted();
37
38         DSTACK(__FUNCTION_NAME);
39
40         BEGIN_DEBUG_EXCEPTION_HANDLER
41
42         while(getRun())
43         {
44                 try{
45                         //TimeTaker timer("AsyncRunStep() + Receive()");
46
47                         {
48                                 //TimeTaker timer("AsyncRunStep()");
49                                 m_server->AsyncRunStep();
50                         }
51                 
52                         //dout_server<<"Running m_server->Receive()"<<std::endl;
53                         m_server->Receive();
54                 }
55                 catch(con::NoIncomingDataException &e)
56                 {
57                 }
58                 catch(con::PeerNotFoundException &e)
59                 {
60                         dout_server<<"Server: PeerNotFoundException"<<std::endl;
61                 }
62         }
63         
64         END_DEBUG_EXCEPTION_HANDLER
65
66         return NULL;
67 }
68
69 void * EmergeThread::Thread()
70 {
71         ThreadStarted();
72
73         DSTACK(__FUNCTION_NAME);
74
75         //bool debug=false;
76         
77         BEGIN_DEBUG_EXCEPTION_HANDLER
78
79         /*
80                 Get block info from queue, emerge them and send them
81                 to clients.
82
83                 After queue is empty, exit.
84         */
85         while(getRun())
86         {
87                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
88                 if(qptr == NULL)
89                         break;
90                 
91                 SharedPtr<QueuedBlockEmerge> q(qptr);
92
93                 v3s16 &p = q->pos;
94                 v2s16 p2d(p.X,p.Z);
95
96                 /*
97                         Do not generate over-limit
98                 */
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)
105                         continue;
106                         
107                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
108
109                 //TimeTaker timer("block emerge");
110                 
111                 /*
112                         Try to emerge it from somewhere.
113
114                         If it is only wanted as optional, only loading from disk
115                         will be allowed.
116                 */
117                 
118                 /*
119                         Check if any peer wants it as non-optional. In that case it
120                         will be generated.
121
122                         Also decrement the emerge queue count in clients.
123                 */
124
125                 bool optional = true;
126
127                 {
128                         core::map<u16, u8>::Iterator i;
129                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
130                         {
131                                 //u16 peer_id = i.getNode()->getKey();
132
133                                 // Check flags
134                                 u8 flags = i.getNode()->getValue();
135                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
136                                         optional = false;
137                                 
138                         }
139                 }
140
141                 /*dstream<<"EmergeThread: p="
142                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
143                                 <<"optional="<<optional<<std::endl;*/
144                 
145                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
146                         
147                 core::map<v3s16, MapBlock*> changed_blocks;
148                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
149
150                 MapBlock *block = NULL;
151                 bool got_block = true;
152                 core::map<v3s16, MapBlock*> modified_blocks;
153                 
154                 bool only_from_disk = false;
155                 
156                 if(optional)
157                         only_from_disk = true;
158
159                 v2s16 chunkpos = map.sector_to_chunk(p2d);
160
161                 bool generate_chunk = false;
162                 if(only_from_disk == false)
163                 {
164                         JMutexAutoLock envlock(m_server->m_env_mutex);
165                         if(map.chunkNonVolatile(chunkpos) == false)
166                                 generate_chunk = true;
167                 }
168                 if(generate_chunk)
169                 {
170                         ChunkMakeData data;
171                         
172                         {
173                                 JMutexAutoLock envlock(m_server->m_env_mutex);
174                                 map.initChunkMake(data, chunkpos);
175                         }
176
177                         makeChunk(&data);
178
179                         {
180                                 JMutexAutoLock envlock(m_server->m_env_mutex);
181                                 map.finishChunkMake(data, changed_blocks);
182                         }
183                 }
184         
185                 /*
186                         Fetch block from map or generate a single block
187                 */
188                 {
189                         JMutexAutoLock envlock(m_server->m_env_mutex);
190                         
191                         // Load sector if it isn't loaded
192                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
193                                 map.loadSectorFull(p2d);
194
195                         block = map.getBlockNoCreateNoEx(p);
196                         if(!block || block->isDummy())
197                         {
198                                 if(only_from_disk)
199                                 {
200                                         got_block = false;
201                                 }
202                                 else
203                                 {
204                                         // Get, load or create sector
205                                         ServerMapSector *sector =
206                                                         (ServerMapSector*)map.createSector(p2d);
207                                         // Generate block
208                                         block = map.generateBlock(p, block, sector, changed_blocks,
209                                                         lighting_invalidated_blocks);
210                                         if(block == NULL)
211                                                 got_block = false;
212                                 }
213                         }
214                         else
215                         {
216                                 if(block->getLightingExpired()){
217                                         lighting_invalidated_blocks[block->getPos()] = block;
218                                 }
219                         }
220
221                         // TODO: Some additional checking and lighting updating,
222                         // see emergeBlock
223                 }
224
225                 {//envlock
226                 JMutexAutoLock envlock(m_server->m_env_mutex);
227                 
228                 if(got_block)
229                 {
230                         /*
231                                 Collect a list of blocks that have been modified in
232                                 addition to the fetched one.
233                         */
234                         
235                         if(lighting_invalidated_blocks.size() > 0)
236                         {
237                                 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
238                                                 <<" blocks"<<std::endl;*/
239                         
240                                 // 50-100ms for single block generation
241                                 //TimeTaker timer("** EmergeThread updateLighting");
242                                 
243                                 // Update lighting without locking the environment mutex,
244                                 // add modified blocks to changed blocks
245                                 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
246                         }
247                                 
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++)
251                         {
252                                 MapBlock *block = i.getNode()->getValue();
253                                 modified_blocks.insert(block->getPos(), block);
254                         }
255                 }
256                 // If we got no block, there should be no invalidated blocks
257                 else
258                 {
259                         assert(lighting_invalidated_blocks.size() == 0);
260                 }
261
262                 }//envlock
263
264                 /*
265                         Set sent status of modified blocks on clients
266                 */
267         
268                 // NOTE: Server's clients are also behind the connection mutex
269                 JMutexAutoLock lock(m_server->m_con_mutex);
270
271                 /*
272                         Add the originally fetched block to the modified list
273                 */
274                 if(got_block)
275                 {
276                         modified_blocks.insert(p, block);
277                 }
278                 
279                 /*
280                         Set the modified blocks unsent for all the clients
281                 */
282                 
283                 for(core::map<u16, RemoteClient*>::Iterator
284                                 i = m_server->m_clients.getIterator();
285                                 i.atEnd() == false; i++)
286                 {
287                         RemoteClient *client = i.getNode()->getValue();
288                         
289                         if(modified_blocks.size() > 0)
290                         {
291                                 // Remove block from sent history
292                                 client->SetBlocksNotSent(modified_blocks);
293                         }
294                 }
295                 
296         }
297
298         END_DEBUG_EXCEPTION_HANDLER
299
300         return NULL;
301 }
302
303 void RemoteClient::GetNextBlocks(Server *server, float dtime,
304                 core::array<PrioritySortedBlockTransfer> &dest)
305 {
306         DSTACK(__FUNCTION_NAME);
307         
308         // Increment timers
309         m_nearest_unsent_reset_timer += dtime;
310
311         // Won't send anything if already sending
312         if(m_blocks_sending.size() >= g_settings.getU16
313                         ("max_simultaneous_block_sends_per_client"))
314         {
315                 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
316                 return;
317         }
318
319         Player *player = server->m_env.getPlayer(peer_id);
320
321         assert(player != NULL);
322
323         v3f playerpos = player->getPosition();
324         v3f playerspeed = player->getSpeed();
325
326         v3s16 center_nodepos = floatToInt(playerpos, BS);
327
328         v3s16 center = getNodeBlockPos(center_nodepos);
329         
330         // Camera position and direction
331         v3f camera_pos =
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());
336
337         /*
338                 Get the starting value of the block finder radius.
339         */
340         s16 last_nearest_unsent_d;
341         s16 d_start;
342                 
343         if(m_last_center != center)
344         {
345                 m_nearest_unsent_d = 0;
346                 m_last_center = center;
347         }
348
349         /*dstream<<"m_nearest_unsent_reset_timer="
350                         <<m_nearest_unsent_reset_timer<<std::endl;*/
351         if(m_nearest_unsent_reset_timer > 5.0)
352         {
353                 m_nearest_unsent_reset_timer = 0;
354                 m_nearest_unsent_d = 0;
355                 //dstream<<"Resetting m_nearest_unsent_d"<<std::endl;
356         }
357
358         last_nearest_unsent_d = m_nearest_unsent_d;
359         
360         d_start = m_nearest_unsent_d;
361
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;
366
367         /*
368                 Check the time from last addNode/removeNode.
369                 
370                 Decrease send rate if player is building stuff.
371         */
372         m_time_from_building += dtime;
373         if(m_time_from_building < g_settings.getFloat(
374                                 "full_block_send_enable_min_time_from_building"))
375         {
376                 maximum_simultaneous_block_sends
377                         = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
378         }
379         
380         u32 num_blocks_selected = m_blocks_sending.size();
381         
382         /*
383                 next time d will be continued from the d from which the nearest
384                 unsent block was found this time.
385
386                 This is because not necessarily any of the blocks found this
387                 time are actually sent.
388         */
389         s32 new_nearest_unsent_d = -1;
390
391         s16 d_max = g_settings.getS16("max_block_send_distance");
392         s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
393         
394         //dstream<<"Starting from "<<d_start<<std::endl;
395
396         for(s16 d = d_start; d <= d_max; d++)
397         {
398                 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
399                 
400                 /*
401                         If m_nearest_unsent_d was changed by the EmergeThread
402                         (it can change it to 0 through SetBlockNotSent),
403                         update our d to it.
404                         Else update m_nearest_unsent_d
405                 */
406                 if(m_nearest_unsent_d != last_nearest_unsent_d)
407                 {
408                         d = m_nearest_unsent_d;
409                         last_nearest_unsent_d = m_nearest_unsent_d;
410                 }
411
412                 /*
413                         Get the border/face dot coordinates of a "d-radiused"
414                         box
415                 */
416                 core::list<v3s16> list;
417                 getFacePositions(list, d);
418                 
419                 core::list<v3s16>::Iterator li;
420                 for(li=list.begin(); li!=list.end(); li++)
421                 {
422                         v3s16 p = *li + center;
423                         
424                         /*
425                                 Send throttling
426                                 - Don't allow too many simultaneous transfers
427                                 - EXCEPT when the blocks are very close
428
429                                 Also, don't send blocks that are already flying.
430                         */
431                         
432                         u16 maximum_simultaneous_block_sends_now =
433                                         maximum_simultaneous_block_sends;
434                         
435                         if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
436                         {
437                                 maximum_simultaneous_block_sends_now =
438                                                 maximum_simultaneous_block_sends_setting;
439                         }
440
441                         // Limit is dynamically lowered when building
442                         if(num_blocks_selected
443                                         >= maximum_simultaneous_block_sends_now)
444                         {
445                                 /*dstream<<"Not sending more blocks. Queue full. "
446                                                 <<m_blocks_sending.size()
447                                                 <<std::endl;*/
448                                 goto queue_full;
449                         }
450
451                         if(m_blocks_sending.find(p) != NULL)
452                                 continue;
453                 
454                         /*
455                                 Do not go over-limit
456                         */
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)
463                                 continue;
464                 
465                         // If this is true, inexistent block will be made from scratch
466                         bool generate = d <= d_max_gen;
467                         
468                         {
469                                 /*// Limit the generating area vertically to 2/3
470                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
471                                         generate = false;*/
472
473                                 // Limit the send area vertically to 2/3
474                                 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
475                                         continue;
476                         }
477
478 #if 0
479                         /*
480                                 If block is far away, don't generate it unless it is
481                                 near ground level
482
483                                 NOTE: We can't know the ground level this way with the
484                                 new generator.
485                         */
486                         if(d > 4)
487                         {
488                                 v2s16 p2d(p.X, p.Z);
489                                 MapSector *sector = NULL;
490                                 try
491                                 {
492                                         sector = server->m_env.getMap().getSectorNoGenerate(p2d);
493                                 }
494                                 catch(InvalidPositionException &e)
495                                 {
496                                 }
497
498                                 if(sector != NULL)
499                                 {
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)
507                                                 generate = false;
508                                 }
509                         }
510 #endif
511
512                         /*
513                                 Don't generate or send if not in sight
514                         */
515
516                         if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
517                         {
518                                 continue;
519                         }
520                         
521                         /*
522                                 Don't send already sent blocks
523                         */
524                         {
525                                 if(m_blocks_sent.find(p) != NULL)
526                                         continue;
527                         }
528
529                         /*
530                                 Check if map has this block
531                         */
532                         MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
533                         
534                         bool surely_not_found_on_disk = false;
535                         bool block_is_invalid = false;
536                         if(block != NULL)
537                         {
538                                 if(block->isDummy())
539                                 {
540                                         surely_not_found_on_disk = true;
541                                 }
542
543                                 if(block->isValid() == false)
544                                 {
545                                         block_is_invalid = true;
546                                 }
547                                 
548                                 /*if(block->isFullyGenerated() == false)
549                                 {
550                                         block_is_invalid = true;
551                                 }*/
552                                 
553                                 v2s16 p2d(p.X, p.Z);
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;
558                         }
559
560                         /*
561                                 If block has been marked to not exist on disk (dummy)
562                                 and generating new ones is not wanted, skip block.
563                         */
564                         if(generate == false && surely_not_found_on_disk == true)
565                         {
566                                 // get next one.
567                                 continue;
568                         }
569
570                         /*
571                                 Record the lowest d from which a a block has been
572                                 found being not sent and possibly to exist
573                         */
574                         if(new_nearest_unsent_d == -1 || d < new_nearest_unsent_d)
575                         {
576                                 new_nearest_unsent_d = d;
577                         }
578                                         
579                         /*
580                                 Add inexistent block to emerge queue.
581                         */
582                         if(block == NULL || surely_not_found_on_disk || block_is_invalid)
583                         {
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)
588                                 {
589                                         //dstream<<"Adding block to emerge queue"<<std::endl;
590                                         
591                                         // Add it to the emerge queue and trigger the thread
592                                         
593                                         u8 flags = 0;
594                                         if(generate == false)
595                                                 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
596                                         
597                                         server->m_emerge_queue.addBlock(peer_id, p, flags);
598                                         server->m_emergethread.trigger();
599                                 }
600                                 
601                                 // get next one.
602                                 continue;
603                         }
604
605                         /*
606                                 Add block to send queue
607                         */
608
609                         PrioritySortedBlockTransfer q((float)d, p, peer_id);
610
611                         dest.push_back(q);
612
613                         num_blocks_selected += 1;
614                 }
615         }
616 queue_full:
617
618         if(new_nearest_unsent_d != -1)
619         {
620                 m_nearest_unsent_d = new_nearest_unsent_d;
621         }
622 }
623
624 void RemoteClient::SendObjectData(
625                 Server *server,
626                 float dtime,
627                 core::map<v3s16, bool> &stepped_blocks
628         )
629 {
630         DSTACK(__FUNCTION_NAME);
631
632         // Can't send anything without knowing version
633         if(serialization_version == SER_FMT_VER_INVALID)
634         {
635                 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
636                                 <<std::endl;
637                 return;
638         }
639
640         /*
641                 Send a TOCLIENT_OBJECTDATA packet.
642                 Sent as unreliable.
643
644                 u16 command
645                 u16 number of player positions
646                 for each player:
647                         v3s32 position*100
648                         v3s32 speed*100
649                         s32 pitch*100
650                         s32 yaw*100
651                 u16 count of blocks
652                 for each block:
653                         block objects
654         */
655
656         std::ostringstream os(std::ios_base::binary);
657         u8 buf[12];
658         
659         // Write command
660         writeU16(buf, TOCLIENT_OBJECTDATA);
661         os.write((char*)buf, 2);
662         
663         /*
664                 Get and write player data
665         */
666         
667         // Get connected players
668         core::list<Player*> players = server->m_env.getPlayers(true);
669
670         // Write player count
671         u16 playercount = players.size();
672         writeU16(buf, playercount);
673         os.write((char*)buf, 2);
674
675         core::list<Player*>::Iterator i;
676         for(i = players.begin();
677                         i != players.end(); i++)
678         {
679                 Player *player = *i;
680
681                 v3f pf = player->getPosition();
682                 v3f sf = player->getSpeed();
683
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);
688                 
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);
699         }
700         
701         /*
702                 Get and write object data
703         */
704
705         /*
706                 Get nearby blocks.
707                 
708                 For making players to be able to build to their nearby
709                 environment (building is not possible on blocks that are not
710                 in memory):
711                 - Set blocks changed
712                 - Add blocks to emerge queue if they are not found
713
714                 SUGGESTION: These could be ignored from the backside of the player
715         */
716
717         Player *player = server->m_env.getPlayer(peer_id);
718
719         assert(player);
720
721         v3f playerpos = player->getPosition();
722         v3f playerspeed = player->getSpeed();
723
724         v3s16 center_nodepos = floatToInt(playerpos, BS);
725         v3s16 center = getNodeBlockPos(center_nodepos);
726
727         s16 d_max = g_settings.getS16("active_object_range");
728         
729         // Number of blocks whose objects were written to bos
730         u16 blockcount = 0;
731
732         std::ostringstream bos(std::ios_base::binary);
733
734         for(s16 d = 0; d <= d_max; d++)
735         {
736                 core::list<v3s16> list;
737                 getFacePositions(list, d);
738                 
739                 core::list<v3s16>::Iterator li;
740                 for(li=list.begin(); li!=list.end(); li++)
741                 {
742                         v3s16 p = *li + center;
743
744                         /*
745                                 Ignore blocks that haven't been sent to the client
746                         */
747                         {
748                                 if(m_blocks_sent.find(p) == NULL)
749                                         continue;
750                         }
751                         
752                         // Try stepping block and add it to a send queue
753                         try
754                         {
755
756                         // Get block
757                         MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
758
759                         /*
760                                 Step block if not in stepped_blocks and add to stepped_blocks.
761                         */
762                         if(stepped_blocks.find(p) == NULL)
763                         {
764                                 block->stepObjects(dtime, true, server->getDayNightRatio());
765                                 stepped_blocks.insert(p, true);
766                                 block->setChangedFlag();
767                         }
768
769                         // Skip block if there are no objects
770                         if(block->getObjectCount() == 0)
771                                 continue;
772                         
773                         /*
774                                 Write objects
775                         */
776
777                         // Write blockpos
778                         writeV3S16(buf, p);
779                         bos.write((char*)buf, 6);
780
781                         // Write objects
782                         block->serializeObjects(bos, serialization_version);
783
784                         blockcount++;
785
786                         /*
787                                 Stop collecting objects if data is already too big
788                         */
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)
793                         {
794                                 goto skip_subsequent;
795                         }
796                         
797                         } //try
798                         catch(InvalidPositionException &e)
799                         {
800                                 // Not in memory
801                                 // Add it to the emerge queue and trigger the thread.
802                                 // Fetch the block only if it is on disk.
803                                 
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++;*/
808                                 
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();
813                         }
814                 }
815         }
816
817 skip_subsequent:
818
819         // Write block count
820         writeU16(buf, blockcount);
821         os.write((char*)buf, 2);
822
823         // Write block objects
824         os<<bos.str();
825
826         /*
827                 Send data
828         */
829         
830         //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
831
832         // Make data buffer
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);
837 }
838
839 void RemoteClient::GotBlock(v3s16 p)
840 {
841         if(m_blocks_sending.find(p) != NULL)
842                 m_blocks_sending.remove(p);
843         else
844         {
845                 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
846                                 " m_blocks_sending"<<std::endl;*/
847                 m_excess_gotblocks++;
848         }
849         m_blocks_sent.insert(p, true);
850 }
851
852 void RemoteClient::SentBlock(v3s16 p)
853 {
854         if(m_blocks_sending.find(p) == NULL)
855                 m_blocks_sending.insert(p, 0.0);
856         else
857                 dstream<<"RemoteClient::SentBlock(): Sent block"
858                                 " already in m_blocks_sending"<<std::endl;
859 }
860
861 void RemoteClient::SetBlockNotSent(v3s16 p)
862 {
863         m_nearest_unsent_d = 0;
864         
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);
869 }
870
871 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
872 {
873         m_nearest_unsent_d = 0;
874         
875         for(core::map<v3s16, MapBlock*>::Iterator
876                         i = blocks.getIterator();
877                         i.atEnd()==false; i++)
878         {
879                 v3s16 p = i.getNode()->getKey();
880
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);
885         }
886 }
887
888 /*
889         PlayerInfo
890 */
891
892 PlayerInfo::PlayerInfo()
893 {
894         name[0] = 0;
895 }
896
897 void PlayerInfo::PrintLine(std::ostream *s)
898 {
899         (*s)<<id<<": ";
900         (*s)<<"\""<<name<<"\" ("
901                         <<(position.X/10)<<","<<(position.Y/10)
902                         <<","<<(position.Z/10)<<") ";
903         address.print(s);
904         (*s)<<" avg_rtt="<<avg_rtt;
905         (*s)<<std::endl;
906 }
907
908 u32 PIChecksum(core::list<PlayerInfo> &l)
909 {
910         core::list<PlayerInfo>::Iterator i;
911         u32 checksum = 1;
912         u32 a = 10;
913         for(i=l.begin(); i!=l.end(); i++)
914         {
915                 checksum += a * (i->id+1);
916                 checksum ^= 0x435aafcd;
917                 a *= 10;
918         }
919         return checksum;
920 }
921
922 /*
923         Server
924 */
925
926 Server::Server(
927                 std::string mapsavedir
928         ):
929         m_env(new ServerMap(mapsavedir), this),
930         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
931         m_thread(this),
932         m_emergethread(this),
933         m_time_of_day(9000),
934         m_time_counter(0),
935         m_time_of_day_send_timer(0),
936         m_uptime(0),
937         m_mapsavedir(mapsavedir),
938         m_shutdown_requested(false),
939         m_ignore_map_edit_events(false),
940         m_ignore_map_edit_events_peer_id(0)
941 {
942         m_liquid_transform_timer = 0.0;
943         m_print_info_timer = 0.0;
944         m_objectdata_timer = 0.0;
945         m_emergethread_trigger_timer = 0.0;
946         m_savemap_timer = 0.0;
947         
948         m_env_mutex.Init();
949         m_con_mutex.Init();
950         m_step_dtime_mutex.Init();
951         m_step_dtime = 0.0;
952
953         m_env.getMap().addEventReceiver(this);
954
955         // Load players
956         m_env.deSerializePlayers(m_mapsavedir);
957 }
958
959 Server::~Server()
960 {
961         /*
962                 Send shutdown message
963         */
964         {
965                 JMutexAutoLock conlock(m_con_mutex);
966                 
967                 std::wstring line = L"*** Server shutting down";
968
969                 /*
970                         Send the message to clients
971                 */
972                 for(core::map<u16, RemoteClient*>::Iterator
973                         i = m_clients.getIterator();
974                         i.atEnd() == false; i++)
975                 {
976                         // Get client and check that it is valid
977                         RemoteClient *client = i.getNode()->getValue();
978                         assert(client->peer_id == i.getNode()->getKey());
979                         if(client->serialization_version == SER_FMT_VER_INVALID)
980                                 continue;
981
982                         SendChatMessage(client->peer_id, line);
983                 }
984         }
985
986         /*
987                 Save players
988         */
989         m_env.serializePlayers(m_mapsavedir);
990         
991         /*
992                 Stop threads
993         */
994         stop();
995         
996         /*
997                 Delete clients
998         */
999         {
1000                 JMutexAutoLock clientslock(m_con_mutex);
1001
1002                 for(core::map<u16, RemoteClient*>::Iterator
1003                         i = m_clients.getIterator();
1004                         i.atEnd() == false; i++)
1005                 {
1006                         /*// Delete player
1007                         // NOTE: These are removed by env destructor
1008                         {
1009                                 u16 peer_id = i.getNode()->getKey();
1010                                 JMutexAutoLock envlock(m_env_mutex);
1011                                 m_env.removePlayer(peer_id);
1012                         }*/
1013                         
1014                         // Delete client
1015                         delete i.getNode()->getValue();
1016                 }
1017         }
1018 }
1019
1020 void Server::start(unsigned short port)
1021 {
1022         DSTACK(__FUNCTION_NAME);
1023         // Stop thread if already running
1024         m_thread.stop();
1025         
1026         // Initialize connection
1027         m_con.setTimeoutMs(30);
1028         m_con.Serve(port);
1029
1030         // Start thread
1031         m_thread.setRun(true);
1032         m_thread.Start();
1033         
1034         dout_server<<"Server: Started on port "<<port<<std::endl;
1035 }
1036
1037 void Server::stop()
1038 {
1039         DSTACK(__FUNCTION_NAME);
1040
1041         // Stop threads (set run=false first so both start stopping)
1042         m_thread.setRun(false);
1043         m_emergethread.setRun(false);
1044         m_thread.stop();
1045         m_emergethread.stop();
1046         
1047         dout_server<<"Server: Threads stopped"<<std::endl;
1048
1049         dout_server<<"Server: Saving players"<<std::endl;
1050         // Save players
1051         // FIXME: Apparently this does not do anything here
1052         //m_env.serializePlayers(m_mapsavedir);
1053 }
1054
1055 void Server::step(float dtime)
1056 {
1057         DSTACK(__FUNCTION_NAME);
1058         // Limit a bit
1059         if(dtime > 2.0)
1060                 dtime = 2.0;
1061         {
1062                 JMutexAutoLock lock(m_step_dtime_mutex);
1063                 m_step_dtime += dtime;
1064         }
1065 }
1066
1067 void Server::AsyncRunStep()
1068 {
1069         DSTACK(__FUNCTION_NAME);
1070         
1071         float dtime;
1072         {
1073                 JMutexAutoLock lock1(m_step_dtime_mutex);
1074                 dtime = m_step_dtime;
1075         }
1076         
1077         // Send blocks to clients
1078         SendBlocks(dtime);
1079         
1080         if(dtime < 0.001)
1081                 return;
1082         
1083         //dstream<<"Server steps "<<dtime<<std::endl;
1084         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1085         
1086         {
1087                 JMutexAutoLock lock1(m_step_dtime_mutex);
1088                 m_step_dtime -= dtime;
1089         }
1090
1091         /*
1092                 Update uptime
1093         */
1094         {
1095                 m_uptime.set(m_uptime.get() + dtime);
1096         }
1097         
1098         /*
1099                 Update m_time_of_day
1100         */
1101         {
1102                 m_time_counter += dtime;
1103                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1104                 u32 units = (u32)(m_time_counter*speed);
1105                 m_time_counter -= (f32)units / speed;
1106                 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1107                 
1108                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1109
1110                 /*
1111                         Send to clients at constant intervals
1112                 */
1113
1114                 m_time_of_day_send_timer -= dtime;
1115                 if(m_time_of_day_send_timer < 0.0)
1116                 {
1117                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1118
1119                         //JMutexAutoLock envlock(m_env_mutex);
1120                         JMutexAutoLock conlock(m_con_mutex);
1121
1122                         for(core::map<u16, RemoteClient*>::Iterator
1123                                 i = m_clients.getIterator();
1124                                 i.atEnd() == false; i++)
1125                         {
1126                                 RemoteClient *client = i.getNode()->getValue();
1127                                 //Player *player = m_env.getPlayer(client->peer_id);
1128                                 
1129                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1130                                                 m_time_of_day.get());
1131                                 // Send as reliable
1132                                 m_con.Send(client->peer_id, 0, data, true);
1133                         }
1134                 }
1135         }
1136
1137         {
1138                 // Process connection's timeouts
1139                 JMutexAutoLock lock2(m_con_mutex);
1140                 m_con.RunTimeouts(dtime);
1141         }
1142         
1143         {
1144                 // This has to be called so that the client list gets synced
1145                 // with the peer list of the connection
1146                 handlePeerChanges();
1147         }
1148
1149         {
1150                 // Step environment
1151                 // This also runs Map's timers
1152                 JMutexAutoLock lock(m_env_mutex);
1153                 m_env.step(dtime);
1154         }
1155         
1156         /*
1157                 Do background stuff
1158         */
1159         
1160         /*
1161                 Transform liquids
1162         */
1163         m_liquid_transform_timer += dtime;
1164         if(m_liquid_transform_timer >= 1.00)
1165         {
1166                 m_liquid_transform_timer -= 1.00;
1167                 
1168                 JMutexAutoLock lock(m_env_mutex);
1169                 
1170                 core::map<v3s16, MapBlock*> modified_blocks;
1171                 m_env.getMap().transformLiquids(modified_blocks);
1172 #if 0           
1173                 /*
1174                         Update lighting
1175                 */
1176                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1177                 ServerMap &map = ((ServerMap&)m_env.getMap());
1178                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1179                 
1180                 // Add blocks modified by lighting to modified_blocks
1181                 for(core::map<v3s16, MapBlock*>::Iterator
1182                                 i = lighting_modified_blocks.getIterator();
1183                                 i.atEnd() == false; i++)
1184                 {
1185                         MapBlock *block = i.getNode()->getValue();
1186                         modified_blocks.insert(block->getPos(), block);
1187                 }
1188 #endif
1189                 /*
1190                         Set the modified blocks unsent for all the clients
1191                 */
1192                 
1193                 JMutexAutoLock lock2(m_con_mutex);
1194
1195                 for(core::map<u16, RemoteClient*>::Iterator
1196                                 i = m_clients.getIterator();
1197                                 i.atEnd() == false; i++)
1198                 {
1199                         RemoteClient *client = i.getNode()->getValue();
1200                         
1201                         if(modified_blocks.size() > 0)
1202                         {
1203                                 // Remove block from sent history
1204                                 client->SetBlocksNotSent(modified_blocks);
1205                         }
1206                 }
1207         }
1208
1209         // Periodically print some info
1210         {
1211                 float &counter = m_print_info_timer;
1212                 counter += dtime;
1213                 if(counter >= 30.0)
1214                 {
1215                         counter = 0.0;
1216
1217                         JMutexAutoLock lock2(m_con_mutex);
1218
1219                         for(core::map<u16, RemoteClient*>::Iterator
1220                                 i = m_clients.getIterator();
1221                                 i.atEnd() == false; i++)
1222                         {
1223                                 //u16 peer_id = i.getNode()->getKey();
1224                                 RemoteClient *client = i.getNode()->getValue();
1225                                 Player *player = m_env.getPlayer(client->peer_id);
1226                                 std::cout<<player->getName()<<"\t";
1227                                 client->PrintInfo(std::cout);
1228                         }
1229                 }
1230         }
1231
1232         //if(g_settings.getBool("enable_experimental"))
1233         {
1234
1235         /*
1236                 Check added and deleted active objects
1237         */
1238         {
1239                 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1240
1241                 JMutexAutoLock envlock(m_env_mutex);
1242                 JMutexAutoLock conlock(m_con_mutex);
1243                 
1244                 // Radius inside which objects are active
1245                 s16 radius = 32;
1246
1247                 for(core::map<u16, RemoteClient*>::Iterator
1248                         i = m_clients.getIterator();
1249                         i.atEnd() == false; i++)
1250                 {
1251                         RemoteClient *client = i.getNode()->getValue();
1252                         Player *player = m_env.getPlayer(client->peer_id);
1253                         if(player==NULL)
1254                         {
1255                                 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1256                                                 <<" has no associated player"<<std::endl;
1257                                 continue;
1258                         }
1259                         v3s16 pos = floatToInt(player->getPosition(), BS);
1260
1261                         core::map<u16, bool> removed_objects;
1262                         core::map<u16, bool> added_objects;
1263                         m_env.getRemovedActiveObjects(pos, radius,
1264                                         client->m_known_objects, removed_objects);
1265                         m_env.getAddedActiveObjects(pos, radius,
1266                                         client->m_known_objects, added_objects);
1267                         
1268                         // Ignore if nothing happened
1269                         if(removed_objects.size() == 0 && added_objects.size() == 0)
1270                         {
1271                                 //dstream<<"INFO: active objects: none changed"<<std::endl;
1272                                 continue;
1273                         }
1274                         
1275                         std::string data_buffer;
1276
1277                         char buf[4];
1278                         
1279                         // Handle removed objects
1280                         writeU16((u8*)buf, removed_objects.size());
1281                         data_buffer.append(buf, 2);
1282                         for(core::map<u16, bool>::Iterator
1283                                         i = removed_objects.getIterator();
1284                                         i.atEnd()==false; i++)
1285                         {
1286                                 // Get object
1287                                 u16 id = i.getNode()->getKey();
1288                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1289
1290                                 // Add to data buffer for sending
1291                                 writeU16((u8*)buf, i.getNode()->getKey());
1292                                 data_buffer.append(buf, 2);
1293                                 
1294                                 // Remove from known objects
1295                                 client->m_known_objects.remove(i.getNode()->getKey());
1296
1297                                 if(obj && obj->m_known_by_count > 0)
1298                                         obj->m_known_by_count--;
1299                         }
1300
1301                         // Handle added objects
1302                         writeU16((u8*)buf, added_objects.size());
1303                         data_buffer.append(buf, 2);
1304                         for(core::map<u16, bool>::Iterator
1305                                         i = added_objects.getIterator();
1306                                         i.atEnd()==false; i++)
1307                         {
1308                                 // Get object
1309                                 u16 id = i.getNode()->getKey();
1310                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1311                                 
1312                                 // Get object type
1313                                 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1314                                 if(obj == NULL)
1315                                         dstream<<"WARNING: "<<__FUNCTION_NAME
1316                                                         <<": NULL object"<<std::endl;
1317                                 else
1318                                         type = obj->getType();
1319
1320                                 // Add to data buffer for sending
1321                                 writeU16((u8*)buf, id);
1322                                 data_buffer.append(buf, 2);
1323                                 writeU8((u8*)buf, type);
1324                                 data_buffer.append(buf, 1);
1325                                 
1326                                 if(obj)
1327                                         data_buffer.append(serializeLongString(
1328                                                         obj->getClientInitializationData()));
1329                                 else
1330                                         data_buffer.append(serializeLongString(""));
1331
1332                                 // Add to known objects
1333                                 client->m_known_objects.insert(i.getNode()->getKey(), false);
1334
1335                                 if(obj)
1336                                         obj->m_known_by_count++;
1337                         }
1338
1339                         // Send packet
1340                         SharedBuffer<u8> reply(2 + data_buffer.size());
1341                         writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1342                         memcpy((char*)&reply[2], data_buffer.c_str(),
1343                                         data_buffer.size());
1344                         // Send as reliable
1345                         m_con.Send(client->peer_id, 0, reply, true);
1346
1347                         dstream<<"INFO: Server: Sent object remove/add: "
1348                                         <<removed_objects.size()<<" removed, "
1349                                         <<added_objects.size()<<" added, "
1350                                         <<"packet size is "<<reply.getSize()<<std::endl;
1351                 }
1352
1353 #if 0
1354                 /*
1355                         Collect a list of all the objects known by the clients
1356                         and report it back to the environment.
1357                 */
1358
1359                 core::map<u16, bool> all_known_objects;
1360
1361                 for(core::map<u16, RemoteClient*>::Iterator
1362                         i = m_clients.getIterator();
1363                         i.atEnd() == false; i++)
1364                 {
1365                         RemoteClient *client = i.getNode()->getValue();
1366                         // Go through all known objects of client
1367                         for(core::map<u16, bool>::Iterator
1368                                         i = client->m_known_objects.getIterator();
1369                                         i.atEnd()==false; i++)
1370                         {
1371                                 u16 id = i.getNode()->getKey();
1372                                 all_known_objects[id] = true;
1373                         }
1374                 }
1375                 
1376                 m_env.setKnownActiveObjects(whatever);
1377 #endif
1378
1379         }
1380
1381         /*
1382                 Send object messages
1383         */
1384         {
1385                 JMutexAutoLock envlock(m_env_mutex);
1386                 JMutexAutoLock conlock(m_con_mutex);
1387
1388                 // Key = object id
1389                 // Value = data sent by object
1390                 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1391
1392                 // Get active object messages from environment
1393                 for(;;)
1394                 {
1395                         ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1396                         if(aom.id == 0)
1397                                 break;
1398                         
1399                         core::list<ActiveObjectMessage>* message_list = NULL;
1400                         core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1401                         n = buffered_messages.find(aom.id);
1402                         if(n == NULL)
1403                         {
1404                                 message_list = new core::list<ActiveObjectMessage>;
1405                                 buffered_messages.insert(aom.id, message_list);
1406                         }
1407                         else
1408                         {
1409                                 message_list = n->getValue();
1410                         }
1411                         message_list->push_back(aom);
1412                 }
1413                 
1414                 // Route data to every client
1415                 for(core::map<u16, RemoteClient*>::Iterator
1416                         i = m_clients.getIterator();
1417                         i.atEnd()==false; i++)
1418                 {
1419                         RemoteClient *client = i.getNode()->getValue();
1420                         std::string reliable_data;
1421                         std::string unreliable_data;
1422                         // Go through all objects in message buffer
1423                         for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1424                                         j = buffered_messages.getIterator();
1425                                         j.atEnd()==false; j++)
1426                         {
1427                                 // If object is not known by client, skip it
1428                                 u16 id = j.getNode()->getKey();
1429                                 if(client->m_known_objects.find(id) == NULL)
1430                                         continue;
1431                                 // Get message list of object
1432                                 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1433                                 // Go through every message
1434                                 for(core::list<ActiveObjectMessage>::Iterator
1435                                                 k = list->begin(); k != list->end(); k++)
1436                                 {
1437                                         // Compose the full new data with header
1438                                         ActiveObjectMessage aom = *k;
1439                                         std::string new_data;
1440                                         // Add object id
1441                                         char buf[2];
1442                                         writeU16((u8*)&buf[0], aom.id);
1443                                         new_data.append(buf, 2);
1444                                         // Add data
1445                                         new_data += serializeString(aom.datastring);
1446                                         // Add data to buffer
1447                                         if(aom.reliable)
1448                                                 reliable_data += new_data;
1449                                         else
1450                                                 unreliable_data += new_data;
1451                                 }
1452                         }
1453                         /*
1454                                 reliable_data and unreliable_data are now ready.
1455                                 Send them.
1456                         */
1457                         if(reliable_data.size() > 0)
1458                         {
1459                                 SharedBuffer<u8> reply(2 + reliable_data.size());
1460                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1461                                 memcpy((char*)&reply[2], reliable_data.c_str(),
1462                                                 reliable_data.size());
1463                                 // Send as reliable
1464                                 m_con.Send(client->peer_id, 0, reply, true);
1465                         }
1466                         if(unreliable_data.size() > 0)
1467                         {
1468                                 SharedBuffer<u8> reply(2 + unreliable_data.size());
1469                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1470                                 memcpy((char*)&reply[2], unreliable_data.c_str(),
1471                                                 unreliable_data.size());
1472                                 // Send as unreliable
1473                                 m_con.Send(client->peer_id, 0, reply, false);
1474                         }
1475
1476                         /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1477                         {
1478                                 dstream<<"INFO: Server: Size of object message data: "
1479                                                 <<"reliable: "<<reliable_data.size()
1480                                                 <<", unreliable: "<<unreliable_data.size()
1481                                                 <<std::endl;
1482                         }*/
1483                 }
1484
1485                 // Clear buffered_messages
1486                 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1487                                 i = buffered_messages.getIterator();
1488                                 i.atEnd()==false; i++)
1489                 {
1490                         delete i.getNode()->getValue();
1491                 }
1492         }
1493
1494         } // enable_experimental
1495
1496         /*
1497                 Send queued-for-sending map edit events.
1498         */
1499         {
1500                 while(m_unsent_map_edit_queue.size() != 0)
1501                 {
1502                         MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1503
1504                         if(event->type == MEET_ADDNODE)
1505                         {
1506                                 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1507                                 sendAddNode(event->p, event->n, event->already_known_by_peer);
1508                         }
1509                         else if(event->type == MEET_REMOVENODE)
1510                         {
1511                                 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1512                                 sendRemoveNode(event->p, event->already_known_by_peer);
1513                         }
1514                         else if(event->type == MEET_OTHER)
1515                         {
1516                                 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1517                                                 <<std::endl;
1518                         }
1519                         else
1520                         {
1521                                 dstream<<"WARNING: Server: Unknown MapEditEvent "
1522                                                 <<((u32)event->type)<<std::endl;
1523                         }
1524
1525                         delete event;
1526                 }
1527         }
1528
1529         /*
1530                 Send object positions
1531                 TODO: Get rid of MapBlockObjects
1532         */
1533         {
1534                 float &counter = m_objectdata_timer;
1535                 counter += dtime;
1536                 if(counter >= g_settings.getFloat("objectdata_interval"))
1537                 {
1538                         JMutexAutoLock lock1(m_env_mutex);
1539                         JMutexAutoLock lock2(m_con_mutex);
1540                         SendObjectData(counter);
1541
1542                         counter = 0.0;
1543                 }
1544         }
1545         
1546         /*
1547                 Step node metadata
1548         */
1549         {
1550                 JMutexAutoLock envlock(m_env_mutex);
1551                 JMutexAutoLock conlock(m_con_mutex);
1552                 
1553                 core::map<v3s16, MapBlock*> changed_blocks;
1554                 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1555
1556                 for(core::map<v3s16, MapBlock*>::Iterator
1557                                 i = changed_blocks.getIterator();
1558                                 i.atEnd() == false; i++)
1559                 {
1560                         MapBlock *block = i.getNode()->getValue();
1561
1562                         for(core::map<u16, RemoteClient*>::Iterator
1563                                 i = m_clients.getIterator();
1564                                 i.atEnd()==false; i++)
1565                         {
1566                                 RemoteClient *client = i.getNode()->getValue();
1567                                 client->SetBlockNotSent(block->getPos());
1568                         }
1569                 }
1570         }
1571                 
1572         /*
1573                 Trigger emergethread (it somehow gets to a non-triggered but
1574                 bysy state sometimes)
1575         */
1576         {
1577                 float &counter = m_emergethread_trigger_timer;
1578                 counter += dtime;
1579                 if(counter >= 2.0)
1580                 {
1581                         counter = 0.0;
1582                         
1583                         m_emergethread.trigger();
1584                 }
1585         }
1586
1587         // Save map
1588         {
1589                 float &counter = m_savemap_timer;
1590                 counter += dtime;
1591                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1592                 {
1593                         counter = 0.0;
1594
1595                         JMutexAutoLock lock(m_env_mutex);
1596
1597                         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1598                         {
1599                                 // Save only changed parts
1600                                 m_env.getMap().save(true);
1601
1602                                 // Delete unused sectors
1603                                 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1604                                                 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1605                                 if(deleted_count > 0)
1606                                 {
1607                                         dout_server<<"Server: Unloaded "<<deleted_count
1608                                                         <<" sectors from memory"<<std::endl;
1609                                 }
1610
1611                                 // Save players
1612                                 m_env.serializePlayers(m_mapsavedir);
1613                         }
1614                 }
1615         }
1616 }
1617
1618 void Server::Receive()
1619 {
1620         DSTACK(__FUNCTION_NAME);
1621         u32 data_maxsize = 10000;
1622         Buffer<u8> data(data_maxsize);
1623         u16 peer_id;
1624         u32 datasize;
1625         try{
1626                 {
1627                         JMutexAutoLock conlock(m_con_mutex);
1628                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1629                 }
1630
1631                 // This has to be called so that the client list gets synced
1632                 // with the peer list of the connection
1633                 handlePeerChanges();
1634
1635                 ProcessData(*data, datasize, peer_id);
1636         }
1637         catch(con::InvalidIncomingDataException &e)
1638         {
1639                 derr_server<<"Server::Receive(): "
1640                                 "InvalidIncomingDataException: what()="
1641                                 <<e.what()<<std::endl;
1642         }
1643         catch(con::PeerNotFoundException &e)
1644         {
1645                 //NOTE: This is not needed anymore
1646                 
1647                 // The peer has been disconnected.
1648                 // Find the associated player and remove it.
1649
1650                 /*JMutexAutoLock envlock(m_env_mutex);
1651
1652                 dout_server<<"ServerThread: peer_id="<<peer_id
1653                                 <<" has apparently closed connection. "
1654                                 <<"Removing player."<<std::endl;
1655
1656                 m_env.removePlayer(peer_id);*/
1657         }
1658 }
1659
1660 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1661 {
1662         DSTACK(__FUNCTION_NAME);
1663         // Environment is locked first.
1664         JMutexAutoLock envlock(m_env_mutex);
1665         JMutexAutoLock conlock(m_con_mutex);
1666         
1667         con::Peer *peer;
1668         try{
1669                 peer = m_con.GetPeer(peer_id);
1670         }
1671         catch(con::PeerNotFoundException &e)
1672         {
1673                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1674                                 <<peer_id<<" not found"<<std::endl;
1675                 return;
1676         }
1677         
1678         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1679
1680         try
1681         {
1682
1683         if(datasize < 2)
1684                 return;
1685
1686         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1687         
1688         if(command == TOSERVER_INIT)
1689         {
1690                 // [0] u16 TOSERVER_INIT
1691                 // [2] u8 SER_FMT_VER_HIGHEST
1692                 // [3] u8[20] player_name
1693
1694                 if(datasize < 3)
1695                         return;
1696
1697                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1698                                 <<peer->id<<std::endl;
1699
1700                 // First byte after command is maximum supported
1701                 // serialization version
1702                 u8 client_max = data[2];
1703                 u8 our_max = SER_FMT_VER_HIGHEST;
1704                 // Use the highest version supported by both
1705                 u8 deployed = core::min_(client_max, our_max);
1706                 // If it's lower than the lowest supported, give up.
1707                 if(deployed < SER_FMT_VER_LOWEST)
1708                         deployed = SER_FMT_VER_INVALID;
1709
1710                 //peer->serialization_version = deployed;
1711                 getClient(peer->id)->pending_serialization_version = deployed;
1712
1713                 if(deployed == SER_FMT_VER_INVALID)
1714                 {
1715                         derr_server<<DTIME<<"Server: Cannot negotiate "
1716                                         "serialization version with peer "
1717                                         <<peer_id<<std::endl;
1718                         return;
1719                 }
1720
1721                 /*
1722                         Set up player
1723                 */
1724                 
1725                 // Get player name
1726                 const u32 playername_size = 20;
1727                 char playername[playername_size];
1728                 for(u32 i=0; i<playername_size-1; i++)
1729                 {
1730                         playername[i] = data[3+i];
1731                 }
1732                 playername[playername_size-1] = 0;
1733                 
1734                 // Get player
1735                 Player *player = emergePlayer(playername, "", peer_id);
1736                 //Player *player = m_env.getPlayer(peer_id);
1737
1738                 /*{
1739                         // DEBUG: Test serialization
1740                         std::ostringstream test_os;
1741                         player->serialize(test_os);
1742                         dstream<<"Player serialization test: \""<<test_os.str()
1743                                         <<"\""<<std::endl;
1744                         std::istringstream test_is(test_os.str());
1745                         player->deSerialize(test_is);
1746                 }*/
1747
1748                 // If failed, cancel
1749                 if(player == NULL)
1750                 {
1751                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1752                                         <<": failed to emerge player"<<std::endl;
1753                         return;
1754                 }
1755
1756                 /*
1757                 // If a client is already connected to the player, cancel
1758                 if(player->peer_id != 0)
1759                 {
1760                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1761                                         <<" tried to connect to "
1762                                         "an already connected player (peer_id="
1763                                         <<player->peer_id<<")"<<std::endl;
1764                         return;
1765                 }
1766                 // Set client of player
1767                 player->peer_id = peer_id;
1768                 */
1769
1770                 // Check if player doesn't exist
1771                 if(player == NULL)
1772                         throw con::InvalidIncomingDataException
1773                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1774
1775                 /*// update name if it was supplied
1776                 if(datasize >= 20+3)
1777                 {
1778                         data[20+3-1] = 0;
1779                         player->updateName((const char*)&data[3]);
1780                 }*/
1781
1782                 // Now answer with a TOCLIENT_INIT
1783                 
1784                 SharedBuffer<u8> reply(2+1+6+8);
1785                 writeU16(&reply[0], TOCLIENT_INIT);
1786                 writeU8(&reply[2], deployed);
1787                 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1788                 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1789                 
1790                 // Send as reliable
1791                 m_con.Send(peer_id, 0, reply, true);
1792
1793                 return;
1794         }
1795         if(command == TOSERVER_INIT2)
1796         {
1797                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1798                                 <<peer->id<<std::endl;
1799
1800
1801                 getClient(peer->id)->serialization_version
1802                                 = getClient(peer->id)->pending_serialization_version;
1803
1804                 /*
1805                         Send some initialization data
1806                 */
1807                 
1808                 // Send player info to all players
1809                 SendPlayerInfos();
1810
1811                 // Send inventory to player
1812                 SendInventory(peer->id);
1813                 
1814                 // Send time of day
1815                 {
1816                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1817                                         m_time_of_day.get());
1818                         m_con.Send(peer->id, 0, data, true);
1819                 }
1820                 
1821                 // Send information about server to player in chat
1822                 SendChatMessage(peer_id, getStatusString());
1823                 
1824                 // Send information about joining in chat
1825                 {
1826                         std::wstring name = L"unknown";
1827                         Player *player = m_env.getPlayer(peer_id);
1828                         if(player != NULL)
1829                                 name = narrow_to_wide(player->getName());
1830                         
1831                         std::wstring message;
1832                         message += L"*** ";
1833                         message += name;
1834                         message += L" joined game";
1835                         BroadcastChatMessage(message);
1836                 }
1837
1838                 return;
1839         }
1840
1841         if(peer_ser_ver == SER_FMT_VER_INVALID)
1842         {
1843                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1844                                 " serialization format invalid or not initialized."
1845                                 " Skipping incoming command="<<command<<std::endl;
1846                 return;
1847         }
1848         
1849         Player *player = m_env.getPlayer(peer_id);
1850
1851         if(player == NULL){
1852                 derr_server<<"Server::ProcessData(): Cancelling: "
1853                                 "No player for peer_id="<<peer_id
1854                                 <<std::endl;
1855                 return;
1856         }
1857         if(command == TOSERVER_PLAYERPOS)
1858         {
1859                 if(datasize < 2+12+12+4+4)
1860                         return;
1861         
1862                 u32 start = 0;
1863                 v3s32 ps = readV3S32(&data[start+2]);
1864                 v3s32 ss = readV3S32(&data[start+2+12]);
1865                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1866                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1867                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1868                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1869                 pitch = wrapDegrees(pitch);
1870                 yaw = wrapDegrees(yaw);
1871                 player->setPosition(position);
1872                 player->setSpeed(speed);
1873                 player->setPitch(pitch);
1874                 player->setYaw(yaw);
1875                 
1876                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1877                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1878                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1879         }
1880         else if(command == TOSERVER_GOTBLOCKS)
1881         {
1882                 if(datasize < 2+1)
1883                         return;
1884                 
1885                 /*
1886                         [0] u16 command
1887                         [2] u8 count
1888                         [3] v3s16 pos_0
1889                         [3+6] v3s16 pos_1
1890                         ...
1891                 */
1892
1893                 u16 count = data[2];
1894                 for(u16 i=0; i<count; i++)
1895                 {
1896                         if((s16)datasize < 2+1+(i+1)*6)
1897                                 throw con::InvalidIncomingDataException
1898                                         ("GOTBLOCKS length is too short");
1899                         v3s16 p = readV3S16(&data[2+1+i*6]);
1900                         /*dstream<<"Server: GOTBLOCKS ("
1901                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1902                         RemoteClient *client = getClient(peer_id);
1903                         client->GotBlock(p);
1904                 }
1905         }
1906         else if(command == TOSERVER_DELETEDBLOCKS)
1907         {
1908                 if(datasize < 2+1)
1909                         return;
1910                 
1911                 /*
1912                         [0] u16 command
1913                         [2] u8 count
1914                         [3] v3s16 pos_0
1915                         [3+6] v3s16 pos_1
1916                         ...
1917                 */
1918
1919                 u16 count = data[2];
1920                 for(u16 i=0; i<count; i++)
1921                 {
1922                         if((s16)datasize < 2+1+(i+1)*6)
1923                                 throw con::InvalidIncomingDataException
1924                                         ("DELETEDBLOCKS length is too short");
1925                         v3s16 p = readV3S16(&data[2+1+i*6]);
1926                         /*dstream<<"Server: DELETEDBLOCKS ("
1927                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1928                         RemoteClient *client = getClient(peer_id);
1929                         client->SetBlockNotSent(p);
1930                 }
1931         }
1932         else if(command == TOSERVER_CLICK_OBJECT)
1933         {
1934                 if(datasize < 13)
1935                         return;
1936
1937                 /*
1938                         [0] u16 command
1939                         [2] u8 button (0=left, 1=right)
1940                         [3] v3s16 block
1941                         [9] s16 id
1942                         [11] u16 item
1943                 */
1944                 u8 button = readU8(&data[2]);
1945                 v3s16 p;
1946                 p.X = readS16(&data[3]);
1947                 p.Y = readS16(&data[5]);
1948                 p.Z = readS16(&data[7]);
1949                 s16 id = readS16(&data[9]);
1950                 //u16 item_i = readU16(&data[11]);
1951
1952                 MapBlock *block = NULL;
1953                 try
1954                 {
1955                         block = m_env.getMap().getBlockNoCreate(p);
1956                 }
1957                 catch(InvalidPositionException &e)
1958                 {
1959                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1960                         return;
1961                 }
1962
1963                 MapBlockObject *obj = block->getObject(id);
1964
1965                 if(obj == NULL)
1966                 {
1967                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1968                         return;
1969                 }
1970
1971                 //TODO: Check that object is reasonably close
1972                 
1973                 // Left click
1974                 if(button == 0)
1975                 {
1976                         InventoryList *ilist = player->inventory.getList("main");
1977                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
1978                         {
1979                         
1980                                 // Skip if inventory has no free space
1981                                 if(ilist->getUsedSlots() == ilist->getSize())
1982                                 {
1983                                         dout_server<<"Player inventory has no free space"<<std::endl;
1984                                         return;
1985                                 }
1986                                 
1987                                 /*
1988                                         Create the inventory item
1989                                 */
1990                                 InventoryItem *item = NULL;
1991                                 // If it is an item-object, take the item from it
1992                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
1993                                 {
1994                                         item = ((ItemObject*)obj)->createInventoryItem();
1995                                 }
1996                                 // Else create an item of the object
1997                                 else
1998                                 {
1999                                         item = new MapBlockObjectItem
2000                                                         (obj->getInventoryString());
2001                                 }
2002                                 
2003                                 // Add to inventory and send inventory
2004                                 ilist->addItem(item);
2005                                 SendInventory(player->peer_id);
2006                         }
2007
2008                         // Remove from block
2009                         block->removeObject(id);
2010                 }
2011         }
2012         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2013         {
2014                 if(datasize < 7)
2015                         return;
2016
2017                 /*
2018                         length: 7
2019                         [0] u16 command
2020                         [2] u8 button (0=left, 1=right)
2021                         [3] u16 id
2022                         [5] u16 item
2023                 */
2024                 u8 button = readU8(&data[2]);
2025                 u16 id = readS16(&data[3]);
2026                 //u16 item_i = readU16(&data[11]);
2027         
2028                 ServerActiveObject *obj = m_env.getActiveObject(id);
2029
2030                 if(obj == NULL)
2031                 {
2032                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2033                                         <<std::endl;
2034                         return;
2035                 }
2036
2037                 //TODO: Check that object is reasonably close
2038                 
2039                 // Left click, pick object up (usually)
2040                 if(button == 0)
2041                 {
2042                         InventoryList *ilist = player->inventory.getList("main");
2043                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2044                         {
2045                         
2046                                 // Skip if inventory has no free space
2047                                 if(ilist->getUsedSlots() == ilist->getSize())
2048                                 {
2049                                         dout_server<<"Player inventory has no free space"<<std::endl;
2050                                         return;
2051                                 }
2052
2053                                 // Skip if object has been removed
2054                                 if(obj->m_removed)
2055                                         return;
2056                                 
2057                                 /*
2058                                         Create the inventory item
2059                                 */
2060                                 InventoryItem *item = obj->createPickedUpItem();
2061                                 
2062                                 if(item)
2063                                 {
2064                                         // Add to inventory and send inventory
2065                                         ilist->addItem(item);
2066                                         SendInventory(player->peer_id);
2067
2068                                         // Remove object from environment
2069                                         obj->m_removed = true;
2070                                 }
2071                         }
2072                 }
2073         }
2074         else if(command == TOSERVER_GROUND_ACTION)
2075         {
2076                 if(datasize < 17)
2077                         return;
2078                 /*
2079                         length: 17
2080                         [0] u16 command
2081                         [2] u8 action
2082                         [3] v3s16 nodepos_undersurface
2083                         [9] v3s16 nodepos_abovesurface
2084                         [15] u16 item
2085                         actions:
2086                         0: start digging
2087                         1: place block
2088                         2: stop digging (all parameters ignored)
2089                         3: digging completed
2090                 */
2091                 u8 action = readU8(&data[2]);
2092                 v3s16 p_under;
2093                 p_under.X = readS16(&data[3]);
2094                 p_under.Y = readS16(&data[5]);
2095                 p_under.Z = readS16(&data[7]);
2096                 v3s16 p_over;
2097                 p_over.X = readS16(&data[9]);
2098                 p_over.Y = readS16(&data[11]);
2099                 p_over.Z = readS16(&data[13]);
2100                 u16 item_i = readU16(&data[15]);
2101
2102                 //TODO: Check that target is reasonably close
2103                 
2104                 /*
2105                         0: start digging
2106                 */
2107                 if(action == 0)
2108                 {
2109                         /*
2110                                 NOTE: This can be used in the future to check if
2111                                 somebody is cheating, by checking the timing.
2112                         */
2113                 } // action == 0
2114
2115                 /*
2116                         2: stop digging
2117                 */
2118                 else if(action == 2)
2119                 {
2120 #if 0
2121                         RemoteClient *client = getClient(peer->id);
2122                         JMutexAutoLock digmutex(client->m_dig_mutex);
2123                         client->m_dig_tool_item = -1;
2124 #endif
2125                 }
2126
2127                 /*
2128                         3: Digging completed
2129                 */
2130                 else if(action == 3)
2131                 {
2132                         // Mandatory parameter; actually used for nothing
2133                         core::map<v3s16, MapBlock*> modified_blocks;
2134
2135                         u8 material;
2136                         u8 mineral = MINERAL_NONE;
2137
2138                         bool cannot_remove_node = false;
2139
2140                         try
2141                         {
2142                                 MapNode n = m_env.getMap().getNode(p_under);
2143                                 // Get mineral
2144                                 mineral = n.getMineral();
2145                                 // Get material at position
2146                                 material = n.d;
2147                                 // If not yet cancelled
2148                                 if(cannot_remove_node == false)
2149                                 {
2150                                         // If it's not diggable, do nothing
2151                                         if(content_diggable(material) == false)
2152                                         {
2153                                                 derr_server<<"Server: Not finishing digging: "
2154                                                                 <<"Node not diggable"
2155                                                                 <<std::endl;
2156                                                 cannot_remove_node = true;
2157                                         }
2158                                 }
2159                                 // If not yet cancelled
2160                                 if(cannot_remove_node == false)
2161                                 {
2162                                         // Get node metadata
2163                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2164                                         if(meta && meta->nodeRemovalDisabled() == true)
2165                                         {
2166                                                 derr_server<<"Server: Not finishing digging: "
2167                                                                 <<"Node metadata disables removal"
2168                                                                 <<std::endl;
2169                                                 cannot_remove_node = true;
2170                                         }
2171                                 }
2172                         }
2173                         catch(InvalidPositionException &e)
2174                         {
2175                                 derr_server<<"Server: Not finishing digging: Node not found."
2176                                                 <<" Adding block to emerge queue."
2177                                                 <<std::endl;
2178                                 m_emerge_queue.addBlock(peer_id,
2179                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2180                                 cannot_remove_node = true;
2181                         }
2182
2183                         /*
2184                                 If node can't be removed, set block to be re-sent to
2185                                 client and quit.
2186                         */
2187                         if(cannot_remove_node)
2188                         {
2189                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2190
2191                                 // Client probably has wrong data.
2192                                 // Set block not sent, so that client will get
2193                                 // a valid one.
2194                                 dstream<<"Client "<<peer_id<<" tried to dig "
2195                                                 <<"node; but node cannot be removed."
2196                                                 <<" setting MapBlock not sent."<<std::endl;
2197                                 RemoteClient *client = getClient(peer_id);
2198                                 v3s16 blockpos = getNodeBlockPos(p_under);
2199                                 client->SetBlockNotSent(blockpos);
2200                                         
2201                                 return;
2202                         }
2203                         
2204                         /*
2205                                 Send the removal to all other clients.
2206                                 - If other player is close, send REMOVENODE
2207                                 - Otherwise set blocks not sent
2208                         */
2209                         core::list<u16> far_players;
2210                         sendRemoveNode(p_under, peer_id, &far_players, 100);
2211                         
2212                         /*
2213                                 Update and send inventory
2214                         */
2215
2216                         if(g_settings.getBool("creative_mode") == false)
2217                         {
2218                                 /*
2219                                         Wear out tool
2220                                 */
2221                                 InventoryList *mlist = player->inventory.getList("main");
2222                                 if(mlist != NULL)
2223                                 {
2224                                         InventoryItem *item = mlist->getItem(item_i);
2225                                         if(item && (std::string)item->getName() == "ToolItem")
2226                                         {
2227                                                 ToolItem *titem = (ToolItem*)item;
2228                                                 std::string toolname = titem->getToolName();
2229
2230                                                 // Get digging properties for material and tool
2231                                                 DiggingProperties prop =
2232                                                                 getDiggingProperties(material, toolname);
2233
2234                                                 if(prop.diggable == false)
2235                                                 {
2236                                                         derr_server<<"Server: WARNING: Player digged"
2237                                                                         <<" with impossible material + tool"
2238                                                                         <<" combination"<<std::endl;
2239                                                 }
2240                                                 
2241                                                 bool weared_out = titem->addWear(prop.wear);
2242
2243                                                 if(weared_out)
2244                                                 {
2245                                                         mlist->deleteItem(item_i);
2246                                                 }
2247                                         }
2248                                 }
2249
2250                                 /*
2251                                         Add dug item to inventory
2252                                 */
2253
2254                                 InventoryItem *item = NULL;
2255
2256                                 if(mineral != MINERAL_NONE)
2257                                         item = getDiggedMineralItem(mineral);
2258                                 
2259                                 // If not mineral
2260                                 if(item == NULL)
2261                                 {
2262                                         std::string &dug_s = content_features(material).dug_item;
2263                                         if(dug_s != "")
2264                                         {
2265                                                 std::istringstream is(dug_s, std::ios::binary);
2266                                                 item = InventoryItem::deSerialize(is);
2267                                         }
2268                                 }
2269                                 
2270                                 if(item != NULL)
2271                                 {
2272                                         // Add a item to inventory
2273                                         player->inventory.addItem("main", item);
2274
2275                                         // Send inventory
2276                                         SendInventory(player->peer_id);
2277                                 }
2278                         }
2279
2280                         /*
2281                                 Remove the node
2282                                 (this takes some time so it is done after the quick stuff)
2283                         */
2284                         m_ignore_map_edit_events = true;
2285                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2286                         m_ignore_map_edit_events = false;
2287                         
2288                         /*
2289                                 Set blocks not sent to far players
2290                         */
2291                         for(core::list<u16>::Iterator
2292                                         i = far_players.begin();
2293                                         i != far_players.end(); i++)
2294                         {
2295                                 u16 peer_id = *i;
2296                                 RemoteClient *client = getClient(peer_id);
2297                                 if(client==NULL)
2298                                         continue;
2299                                 client->SetBlocksNotSent(modified_blocks);
2300                         }
2301                 }
2302                 
2303                 /*
2304                         1: place block
2305                 */
2306                 else if(action == 1)
2307                 {
2308
2309                         InventoryList *ilist = player->inventory.getList("main");
2310                         if(ilist == NULL)
2311                                 return;
2312
2313                         // Get item
2314                         InventoryItem *item = ilist->getItem(item_i);
2315                         
2316                         // If there is no item, it is not possible to add it anywhere
2317                         if(item == NULL)
2318                                 return;
2319                         
2320                         /*
2321                                 Handle material items
2322                         */
2323                         if(std::string("MaterialItem") == item->getName())
2324                         {
2325                                 try{
2326                                         // Don't add a node if this is not a free space
2327                                         MapNode n2 = m_env.getMap().getNode(p_over);
2328                                         if(content_buildable_to(n2.d) == false)
2329                                         {
2330                                                 // Client probably has wrong data.
2331                                                 // Set block not sent, so that client will get
2332                                                 // a valid one.
2333                                                 dstream<<"Client "<<peer_id<<" tried to place"
2334                                                                 <<" node in invalid position; setting"
2335                                                                 <<" MapBlock not sent."<<std::endl;
2336                                                 RemoteClient *client = getClient(peer_id);
2337                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2338                                                 client->SetBlockNotSent(blockpos);
2339                                                 return;
2340                                         }
2341                                 }
2342                                 catch(InvalidPositionException &e)
2343                                 {
2344                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2345                                                         <<" Adding block to emerge queue."
2346                                                         <<std::endl;
2347                                         m_emerge_queue.addBlock(peer_id,
2348                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2349                                         return;
2350                                 }
2351
2352                                 // Reset build time counter
2353                                 getClient(peer->id)->m_time_from_building = 0.0;
2354                                 
2355                                 // Create node data
2356                                 MaterialItem *mitem = (MaterialItem*)item;
2357                                 MapNode n;
2358                                 n.d = mitem->getMaterial();
2359                                 if(content_features(n.d).wall_mounted)
2360                                         n.dir = packDir(p_under - p_over);
2361                                 
2362                                 /*
2363                                         Send to all players
2364                                 */
2365                                 core::list<u16> far_players;
2366                                 sendAddNode(p_over, n, 0, &far_players, 100);
2367                                 
2368                                 /*
2369                                         Handle inventory
2370                                 */
2371                                 InventoryList *ilist = player->inventory.getList("main");
2372                                 if(g_settings.getBool("creative_mode") == false && ilist)
2373                                 {
2374                                         // Remove from inventory and send inventory
2375                                         if(mitem->getCount() == 1)
2376                                                 ilist->deleteItem(item_i);
2377                                         else
2378                                                 mitem->remove(1);
2379                                         // Send inventory
2380                                         SendInventory(peer_id);
2381                                 }
2382                                 
2383                                 /*
2384                                         Add node.
2385
2386                                         This takes some time so it is done after the quick stuff
2387                                 */
2388                                 core::map<v3s16, MapBlock*> modified_blocks;
2389                                 m_ignore_map_edit_events = true;
2390                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2391                                 m_ignore_map_edit_events = false;
2392                                 
2393                                 /*
2394                                         Set blocks not sent to far players
2395                                 */
2396                                 for(core::list<u16>::Iterator
2397                                                 i = far_players.begin();
2398                                                 i != far_players.end(); i++)
2399                                 {
2400                                         u16 peer_id = *i;
2401                                         RemoteClient *client = getClient(peer_id);
2402                                         if(client==NULL)
2403                                                 continue;
2404                                         client->SetBlocksNotSent(modified_blocks);
2405                                 }
2406
2407                                 /*
2408                                         Calculate special events
2409                                 */
2410                                 
2411                                 /*if(n.d == CONTENT_MESE)
2412                                 {
2413                                         u32 count = 0;
2414                                         for(s16 z=-1; z<=1; z++)
2415                                         for(s16 y=-1; y<=1; y++)
2416                                         for(s16 x=-1; x<=1; x++)
2417                                         {
2418                                                 
2419                                         }
2420                                 }*/
2421                         }
2422                         /*
2423                                 Place other item (not a block)
2424                         */
2425                         else
2426                         {
2427                                 v3s16 blockpos = getNodeBlockPos(p_over);
2428                                 
2429                                 /*
2430                                         Check that the block is loaded so that the item
2431                                         can properly be added to the static list too
2432                                 */
2433                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2434                                 if(block==NULL)
2435                                 {
2436                                         derr_server<<"Error while placing object: "
2437                                                         "block not found"<<std::endl;
2438                                         return;
2439                                 }
2440
2441                                 dout_server<<"Placing a miscellaneous item on map"
2442                                                 <<std::endl;
2443                                 
2444                                 // Calculate a position for it
2445                                 v3f pos = intToFloat(p_over, BS);
2446                                 //pos.Y -= BS*0.45;
2447                                 pos.Y -= BS*0.25; // let it drop a bit
2448                                 // Randomize a bit
2449                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2450                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2451
2452                                 /*
2453                                         Create the object
2454                                 */
2455                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2456
2457                                 if(obj == NULL)
2458                                 {
2459                                         derr_server<<"WARNING: item resulted in NULL object, "
2460                                                         <<"not placing onto map"
2461                                                         <<std::endl;
2462                                 }
2463                                 else
2464                                 {
2465                                         // Add the object to the environment
2466                                         m_env.addActiveObject(obj);
2467                                         
2468                                         dout_server<<"Placed object"<<std::endl;
2469
2470                                         // If item has count<=1, delete it
2471                                         if(item->getCount() <= 1)
2472                                         {
2473                                                 InventoryList *ilist = player->inventory.getList("main");
2474                                                 if(g_settings.getBool("creative_mode") == false && ilist)
2475                                                 {
2476                                                         // Remove from inventory and send inventory
2477                                                         ilist->deleteItem(item_i);
2478                                                         // Send inventory
2479                                                         SendInventory(peer_id);
2480                                                 }
2481                                         }
2482                                         // Else decrement it
2483                                         else
2484                                         {
2485                                                 item->remove(1);
2486                                                 // Send inventory
2487                                                 SendInventory(peer_id);
2488                                         }
2489                                 }
2490                         }
2491
2492                 } // action == 1
2493
2494                 /*
2495                         Catch invalid actions
2496                 */
2497                 else
2498                 {
2499                         derr_server<<"WARNING: Server: Invalid action "
2500                                         <<action<<std::endl;
2501                 }
2502         }
2503 #if 0
2504         else if(command == TOSERVER_RELEASE)
2505         {
2506                 if(datasize < 3)
2507                         return;
2508                 /*
2509                         length: 3
2510                         [0] u16 command
2511                         [2] u8 button
2512                 */
2513                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2514         }
2515 #endif
2516         else if(command == TOSERVER_SIGNTEXT)
2517         {
2518                 /*
2519                         u16 command
2520                         v3s16 blockpos
2521                         s16 id
2522                         u16 textlen
2523                         textdata
2524                 */
2525                 std::string datastring((char*)&data[2], datasize-2);
2526                 std::istringstream is(datastring, std::ios_base::binary);
2527                 u8 buf[6];
2528                 // Read stuff
2529                 is.read((char*)buf, 6);
2530                 v3s16 blockpos = readV3S16(buf);
2531                 is.read((char*)buf, 2);
2532                 s16 id = readS16(buf);
2533                 is.read((char*)buf, 2);
2534                 u16 textlen = readU16(buf);
2535                 std::string text;
2536                 for(u16 i=0; i<textlen; i++)
2537                 {
2538                         is.read((char*)buf, 1);
2539                         text += (char)buf[0];
2540                 }
2541
2542                 MapBlock *block = NULL;
2543                 try
2544                 {
2545                         block = m_env.getMap().getBlockNoCreate(blockpos);
2546                 }
2547                 catch(InvalidPositionException &e)
2548                 {
2549                         derr_server<<"Error while setting sign text: "
2550                                         "block not found"<<std::endl;
2551                         return;
2552                 }
2553
2554                 MapBlockObject *obj = block->getObject(id);
2555                 if(obj == NULL)
2556                 {
2557                         derr_server<<"Error while setting sign text: "
2558                                         "object not found"<<std::endl;
2559                         return;
2560                 }
2561                 
2562                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2563                 {
2564                         derr_server<<"Error while setting sign text: "
2565                                         "object is not a sign"<<std::endl;
2566                         return;
2567                 }
2568
2569                 ((SignObject*)obj)->setText(text);
2570
2571                 obj->getBlock()->setChangedFlag();
2572         }
2573         else if(command == TOSERVER_SIGNNODETEXT)
2574         {
2575                 /*
2576                         u16 command
2577                         v3s16 p
2578                         u16 textlen
2579                         textdata
2580                 */
2581                 std::string datastring((char*)&data[2], datasize-2);
2582                 std::istringstream is(datastring, std::ios_base::binary);
2583                 u8 buf[6];
2584                 // Read stuff
2585                 is.read((char*)buf, 6);
2586                 v3s16 p = readV3S16(buf);
2587                 is.read((char*)buf, 2);
2588                 u16 textlen = readU16(buf);
2589                 std::string text;
2590                 for(u16 i=0; i<textlen; i++)
2591                 {
2592                         is.read((char*)buf, 1);
2593                         text += (char)buf[0];
2594                 }
2595
2596                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2597                 if(!meta)
2598                         return;
2599                 if(meta->typeId() != CONTENT_SIGN_WALL)
2600                         return;
2601                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2602                 signmeta->setText(text);
2603                 
2604                 v3s16 blockpos = getNodeBlockPos(p);
2605                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2606                 if(block)
2607                 {
2608                         block->setChangedFlag();
2609                 }
2610
2611                 for(core::map<u16, RemoteClient*>::Iterator
2612                         i = m_clients.getIterator();
2613                         i.atEnd()==false; i++)
2614                 {
2615                         RemoteClient *client = i.getNode()->getValue();
2616                         client->SetBlockNotSent(blockpos);
2617                 }
2618         }
2619         else if(command == TOSERVER_INVENTORY_ACTION)
2620         {
2621                 /*// Ignore inventory changes if in creative mode
2622                 if(g_settings.getBool("creative_mode") == true)
2623                 {
2624                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2625                                         <<std::endl;
2626                         return;
2627                 }*/
2628                 // Strip command and create a stream
2629                 std::string datastring((char*)&data[2], datasize-2);
2630                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2631                 std::istringstream is(datastring, std::ios_base::binary);
2632                 // Create an action
2633                 InventoryAction *a = InventoryAction::deSerialize(is);
2634                 if(a != NULL)
2635                 {
2636                         // Create context
2637                         InventoryContext c;
2638                         c.current_player = player;
2639
2640                         /*
2641                                 Handle craftresult specially if not in creative mode
2642                         */
2643                         bool disable_action = false;
2644                         if(a->getType() == IACTION_MOVE
2645                                         && g_settings.getBool("creative_mode") == false)
2646                         {
2647                                 IMoveAction *ma = (IMoveAction*)a;
2648                                 if(ma->to_inv == "current_player" &&
2649                                                 ma->from_inv == "current_player")
2650                                 {
2651                                         // Don't allow moving anything to craftresult
2652                                         if(ma->to_list == "craftresult")
2653                                         {
2654                                                 // Do nothing
2655                                                 disable_action = true;
2656                                         }
2657                                         // When something is removed from craftresult
2658                                         if(ma->from_list == "craftresult")
2659                                         {
2660                                                 disable_action = true;
2661                                                 // Remove stuff from craft
2662                                                 InventoryList *clist = player->inventory.getList("craft");
2663                                                 if(clist)
2664                                                 {
2665                                                         u16 count = ma->count;
2666                                                         if(count == 0)
2667                                                                 count = 1;
2668                                                         clist->decrementMaterials(count);
2669                                                 }
2670                                                 // Do action
2671                                                 // Feed action to player inventory
2672                                                 //a->apply(&player->inventory);
2673                                                 a->apply(&c, this);
2674                                                 // Eat it
2675                                                 delete a;
2676                                                 // If something appeared in craftresult, throw it
2677                                                 // in the main list
2678                                                 InventoryList *rlist = player->inventory.getList("craftresult");
2679                                                 InventoryList *mlist = player->inventory.getList("main");
2680                                                 if(rlist && mlist && rlist->getUsedSlots() == 1)
2681                                                 {
2682                                                         InventoryItem *item1 = rlist->changeItem(0, NULL);
2683                                                         mlist->addItem(item1);
2684                                                 }
2685                                         }
2686                                 }
2687                         }
2688                         
2689                         if(disable_action == false)
2690                         {
2691                                 // Feed action to player inventory
2692                                 //a->apply(&player->inventory);
2693                                 a->apply(&c, this);
2694                                 // Eat the action
2695                                 delete a;
2696                         }
2697                         else
2698                         {
2699                                 // Send inventory
2700                                 SendInventory(player->peer_id);
2701                         }
2702                 }
2703                 else
2704                 {
2705                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2706                                         <<"InventoryAction::deSerialize() returned NULL"
2707                                         <<std::endl;
2708                 }
2709         }
2710         else if(command == TOSERVER_CHAT_MESSAGE)
2711         {
2712                 /*
2713                         u16 command
2714                         u16 length
2715                         wstring message
2716                 */
2717                 u8 buf[6];
2718                 std::string datastring((char*)&data[2], datasize-2);
2719                 std::istringstream is(datastring, std::ios_base::binary);
2720                 
2721                 // Read stuff
2722                 is.read((char*)buf, 2);
2723                 u16 len = readU16(buf);
2724                 
2725                 std::wstring message;
2726                 for(u16 i=0; i<len; i++)
2727                 {
2728                         is.read((char*)buf, 2);
2729                         message += (wchar_t)readU16(buf);
2730                 }
2731
2732                 // Get player name of this client
2733                 std::wstring name = narrow_to_wide(player->getName());
2734                 
2735                 // Line to send to players
2736                 std::wstring line;
2737                 // Whether to send to the player that sent the line
2738                 bool send_to_sender = false;
2739                 // Whether to send to other players
2740                 bool send_to_others = false;
2741                 
2742                 // Parse commands
2743                 std::wstring commandprefix = L"/#";
2744                 if(message.substr(0, commandprefix.size()) == commandprefix)
2745                 {
2746                         line += L"Server: ";
2747
2748                         message = message.substr(commandprefix.size());
2749                         // Get player name as narrow string
2750                         std::string name_s = player->getName();
2751                         // Convert message to narrow string
2752                         std::string message_s = wide_to_narrow(message);
2753                         // Operator is the single name defined in config.
2754                         std::string operator_name = g_settings.get("name");
2755                         bool is_operator = (operator_name != "" &&
2756                                         wide_to_narrow(name) == operator_name);
2757                         bool valid_command = false;
2758                         if(message_s == "help")
2759                         {
2760                                 line += L"-!- Available commands: ";
2761                                 line += L"status ";
2762                                 if(is_operator)
2763                                 {
2764                                         line += L"shutdown setting ";
2765                                 }
2766                                 else
2767                                 {
2768                                 }
2769                                 send_to_sender = true;
2770                                 valid_command = true;
2771                         }
2772                         else if(message_s == "status")
2773                         {
2774                                 line = getStatusString();
2775                                 send_to_sender = true;
2776                                 valid_command = true;
2777                         }
2778                         else if(is_operator)
2779                         {
2780                                 if(message_s == "shutdown")
2781                                 {
2782                                         dstream<<DTIME<<" Server: Operator requested shutdown."
2783                                                         <<std::endl;
2784                                         m_shutdown_requested.set(true);
2785                                         
2786                                         line += L"*** Server shutting down (operator request)";
2787                                         send_to_sender = true;
2788                                         valid_command = true;
2789                                 }
2790                                 else if(message_s.substr(0,8) == "setting ")
2791                                 {
2792                                         std::string confline = message_s.substr(8);
2793                                         g_settings.parseConfigLine(confline);
2794                                         line += L"-!- Setting changed.";
2795                                         send_to_sender = true;
2796                                         valid_command = true;
2797                                 }
2798                         }
2799                         
2800                         if(valid_command == false)
2801                         {
2802                                 line += L"-!- Invalid command: " + message;
2803                                 send_to_sender = true;
2804                         }
2805                 }
2806                 else
2807                 {
2808                         line += L"<";
2809                         /*if(is_operator)
2810                                 line += L"@";*/
2811                         line += name;
2812                         line += L"> ";
2813                         line += message;
2814                         send_to_others = true;
2815                 }
2816                 
2817                 if(line != L"")
2818                 {
2819                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2820
2821                         /*
2822                                 Send the message to clients
2823                         */
2824                         for(core::map<u16, RemoteClient*>::Iterator
2825                                 i = m_clients.getIterator();
2826                                 i.atEnd() == false; i++)
2827                         {
2828                                 // Get client and check that it is valid
2829                                 RemoteClient *client = i.getNode()->getValue();
2830                                 assert(client->peer_id == i.getNode()->getKey());
2831                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2832                                         continue;
2833
2834                                 // Filter recipient
2835                                 bool sender_selected = (peer_id == client->peer_id);
2836                                 if(sender_selected == true && send_to_sender == false)
2837                                         continue;
2838                                 if(sender_selected == false && send_to_others == false)
2839                                         continue;
2840
2841                                 SendChatMessage(client->peer_id, line);
2842                         }
2843                 }
2844         }
2845         else
2846         {
2847                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2848                                 "unknown command "<<command<<std::endl;
2849         }
2850         
2851         } //try
2852         catch(SendFailedException &e)
2853         {
2854                 derr_server<<"Server::ProcessData(): SendFailedException: "
2855                                 <<"what="<<e.what()
2856                                 <<std::endl;
2857         }
2858 }
2859
2860 void Server::onMapEditEvent(MapEditEvent *event)
2861 {
2862         dstream<<"Server::onMapEditEvent()"<<std::endl;
2863         if(m_ignore_map_edit_events)
2864                 return;
2865         MapEditEvent *e = event->clone();
2866         m_unsent_map_edit_queue.push_back(e);
2867 }
2868
2869 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2870 {
2871         if(id == "current_player")
2872         {
2873                 assert(c->current_player);
2874                 return &(c->current_player->inventory);
2875         }
2876         
2877         Strfnd fn(id);
2878         std::string id0 = fn.next(":");
2879
2880         if(id0 == "nodemeta")
2881         {
2882                 v3s16 p;
2883                 p.X = stoi(fn.next(","));
2884                 p.Y = stoi(fn.next(","));
2885                 p.Z = stoi(fn.next(","));
2886                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2887                 if(meta)
2888                         return meta->getInventory();
2889                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2890                                 <<"no metadata found"<<std::endl;
2891                 return NULL;
2892         }
2893
2894         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2895         return NULL;
2896 }
2897 void Server::inventoryModified(InventoryContext *c, std::string id)
2898 {
2899         if(id == "current_player")
2900         {
2901                 assert(c->current_player);
2902                 // Send inventory
2903                 SendInventory(c->current_player->peer_id);
2904                 return;
2905         }
2906         
2907         Strfnd fn(id);
2908         std::string id0 = fn.next(":");
2909
2910         if(id0 == "nodemeta")
2911         {
2912                 v3s16 p;
2913                 p.X = stoi(fn.next(","));
2914                 p.Y = stoi(fn.next(","));
2915                 p.Z = stoi(fn.next(","));
2916                 v3s16 blockpos = getNodeBlockPos(p);
2917
2918                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2919                 if(meta)
2920                         meta->inventoryModified();
2921
2922                 for(core::map<u16, RemoteClient*>::Iterator
2923                         i = m_clients.getIterator();
2924                         i.atEnd()==false; i++)
2925                 {
2926                         RemoteClient *client = i.getNode()->getValue();
2927                         client->SetBlockNotSent(blockpos);
2928                 }
2929
2930                 return;
2931         }
2932
2933         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2934 }
2935
2936 core::list<PlayerInfo> Server::getPlayerInfo()
2937 {
2938         DSTACK(__FUNCTION_NAME);
2939         JMutexAutoLock envlock(m_env_mutex);
2940         JMutexAutoLock conlock(m_con_mutex);
2941         
2942         core::list<PlayerInfo> list;
2943
2944         core::list<Player*> players = m_env.getPlayers();
2945         
2946         core::list<Player*>::Iterator i;
2947         for(i = players.begin();
2948                         i != players.end(); i++)
2949         {
2950                 PlayerInfo info;
2951
2952                 Player *player = *i;
2953
2954                 try{
2955                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2956                         // Copy info from peer to info struct
2957                         info.id = peer->id;
2958                         info.address = peer->address;
2959                         info.avg_rtt = peer->avg_rtt;
2960                 }
2961                 catch(con::PeerNotFoundException &e)
2962                 {
2963                         // Set dummy peer info
2964                         info.id = 0;
2965                         info.address = Address(0,0,0,0,0);
2966                         info.avg_rtt = 0.0;
2967                 }
2968
2969                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2970                 info.position = player->getPosition();
2971
2972                 list.push_back(info);
2973         }
2974
2975         return list;
2976 }
2977
2978
2979 void Server::peerAdded(con::Peer *peer)
2980 {
2981         DSTACK(__FUNCTION_NAME);
2982         dout_server<<"Server::peerAdded(): peer->id="
2983                         <<peer->id<<std::endl;
2984         
2985         PeerChange c;
2986         c.type = PEER_ADDED;
2987         c.peer_id = peer->id;
2988         c.timeout = false;
2989         m_peer_change_queue.push_back(c);
2990 }
2991
2992 void Server::deletingPeer(con::Peer *peer, bool timeout)
2993 {
2994         DSTACK(__FUNCTION_NAME);
2995         dout_server<<"Server::deletingPeer(): peer->id="
2996                         <<peer->id<<", timeout="<<timeout<<std::endl;
2997         
2998         PeerChange c;
2999         c.type = PEER_REMOVED;
3000         c.peer_id = peer->id;
3001         c.timeout = timeout;
3002         m_peer_change_queue.push_back(c);
3003 }
3004
3005 void Server::SendObjectData(float dtime)
3006 {
3007         DSTACK(__FUNCTION_NAME);
3008
3009         core::map<v3s16, bool> stepped_blocks;
3010         
3011         for(core::map<u16, RemoteClient*>::Iterator
3012                 i = m_clients.getIterator();
3013                 i.atEnd() == false; i++)
3014         {
3015                 u16 peer_id = i.getNode()->getKey();
3016                 RemoteClient *client = i.getNode()->getValue();
3017                 assert(client->peer_id == peer_id);
3018                 
3019                 if(client->serialization_version == SER_FMT_VER_INVALID)
3020                         continue;
3021                 
3022                 client->SendObjectData(this, dtime, stepped_blocks);
3023         }
3024 }
3025
3026 void Server::SendPlayerInfos()
3027 {
3028         DSTACK(__FUNCTION_NAME);
3029
3030         //JMutexAutoLock envlock(m_env_mutex);
3031         
3032         // Get connected players
3033         core::list<Player*> players = m_env.getPlayers(true);
3034         
3035         u32 player_count = players.getSize();
3036         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3037
3038         SharedBuffer<u8> data(datasize);
3039         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3040         
3041         u32 start = 2;
3042         core::list<Player*>::Iterator i;
3043         for(i = players.begin();
3044                         i != players.end(); i++)
3045         {
3046                 Player *player = *i;
3047
3048                 /*dstream<<"Server sending player info for player with "
3049                                 "peer_id="<<player->peer_id<<std::endl;*/
3050                 
3051                 writeU16(&data[start], player->peer_id);
3052                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3053                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3054                 start += 2+PLAYERNAME_SIZE;
3055         }
3056
3057         //JMutexAutoLock conlock(m_con_mutex);
3058
3059         // Send as reliable
3060         m_con.SendToAll(0, data, true);
3061 }
3062
3063 void Server::SendInventory(u16 peer_id)
3064 {
3065         DSTACK(__FUNCTION_NAME);
3066         
3067         Player* player = m_env.getPlayer(peer_id);
3068
3069         /*
3070                 Calculate crafting stuff
3071         */
3072         if(g_settings.getBool("creative_mode") == false)
3073         {
3074                 InventoryList *clist = player->inventory.getList("craft");
3075                 InventoryList *rlist = player->inventory.getList("craftresult");
3076                 if(rlist)
3077                 {
3078                         rlist->clearItems();
3079                 }
3080                 if(clist && rlist)
3081                 {
3082                         InventoryItem *items[9];
3083                         for(u16 i=0; i<9; i++)
3084                         {
3085                                 items[i] = clist->getItem(i);
3086                         }
3087                         
3088                         bool found = false;
3089
3090                         // Wood
3091                         if(!found)
3092                         {
3093                                 ItemSpec specs[9];
3094                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3095                                 if(checkItemCombination(items, specs))
3096                                 {
3097                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3098                                         found = true;
3099                                 }
3100                         }
3101
3102                         // Stick
3103                         if(!found)
3104                         {
3105                                 ItemSpec specs[9];
3106                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3107                                 if(checkItemCombination(items, specs))
3108                                 {
3109                                         rlist->addItem(new CraftItem("Stick", 4));
3110                                         found = true;
3111                                 }
3112                         }
3113
3114                         // Sign
3115                         if(!found)
3116                         {
3117                                 ItemSpec specs[9];
3118                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3119                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3120                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3121                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3122                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3123                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3124                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3125                                 if(checkItemCombination(items, specs))
3126                                 {
3127                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3128                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3129                                         found = true;
3130                                 }
3131                         }
3132
3133                         // Torch
3134                         if(!found)
3135                         {
3136                                 ItemSpec specs[9];
3137                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3138                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3139                                 if(checkItemCombination(items, specs))
3140                                 {
3141                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3142                                         found = true;
3143                                 }
3144                         }
3145
3146                         // Wooden pick
3147                         if(!found)
3148                         {
3149                                 ItemSpec specs[9];
3150                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3151                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3152                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3153                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3154                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3155                                 if(checkItemCombination(items, specs))
3156                                 {
3157                                         rlist->addItem(new ToolItem("WPick", 0));
3158                                         found = true;
3159                                 }
3160                         }
3161
3162                         // Stone pick
3163                         if(!found)
3164                         {
3165                                 ItemSpec specs[9];
3166                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3167                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3168                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3169                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3170                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3171                                 if(checkItemCombination(items, specs))
3172                                 {
3173                                         rlist->addItem(new ToolItem("STPick", 0));
3174                                         found = true;
3175                                 }
3176                         }
3177
3178                         // Steel pick
3179                         if(!found)
3180                         {
3181                                 ItemSpec specs[9];
3182                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3183                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3184                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3185                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3186                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3187                                 if(checkItemCombination(items, specs))
3188                                 {
3189                                         rlist->addItem(new ToolItem("SteelPick", 0));
3190                                         found = true;
3191                                 }
3192                         }
3193
3194                         // Mese pick
3195                         if(!found)
3196                         {
3197                                 ItemSpec specs[9];
3198                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3199                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3200                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3201                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3202                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3203                                 if(checkItemCombination(items, specs))
3204                                 {
3205                                         rlist->addItem(new ToolItem("MesePick", 0));
3206                                         found = true;
3207                                 }
3208                         }
3209
3210                         // Wooden showel
3211                         if(!found)
3212                         {
3213                                 ItemSpec specs[9];
3214                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3215                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3216                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3217                                 if(checkItemCombination(items, specs))
3218                                 {
3219                                         rlist->addItem(new ToolItem("WShovel", 0));
3220                                         found = true;
3221                                 }
3222                         }
3223
3224                         // Stone showel
3225                         if(!found)
3226                         {
3227                                 ItemSpec specs[9];
3228                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3229                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3230                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3231                                 if(checkItemCombination(items, specs))
3232                                 {
3233                                         rlist->addItem(new ToolItem("STShovel", 0));
3234                                         found = true;
3235                                 }
3236                         }
3237
3238                         // Steel showel
3239                         if(!found)
3240                         {
3241                                 ItemSpec specs[9];
3242                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3243                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3244                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3245                                 if(checkItemCombination(items, specs))
3246                                 {
3247                                         rlist->addItem(new ToolItem("SteelShovel", 0));
3248                                         found = true;
3249                                 }
3250                         }
3251
3252                         // Wooden axe
3253                         if(!found)
3254                         {
3255                                 ItemSpec specs[9];
3256                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3257                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3258                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3259                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3260                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3261                                 if(checkItemCombination(items, specs))
3262                                 {
3263                                         rlist->addItem(new ToolItem("WAxe", 0));
3264                                         found = true;
3265                                 }
3266                         }
3267
3268                         // Stone axe
3269                         if(!found)
3270                         {
3271                                 ItemSpec specs[9];
3272                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3273                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3274                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3275                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3276                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3277                                 if(checkItemCombination(items, specs))
3278                                 {
3279                                         rlist->addItem(new ToolItem("STAxe", 0));
3280                                         found = true;
3281                                 }
3282                         }
3283
3284                         // Steel axe
3285                         if(!found)
3286                         {
3287                                 ItemSpec specs[9];
3288                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3289                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3290                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3291                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3292                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3293                                 if(checkItemCombination(items, specs))
3294                                 {
3295                                         rlist->addItem(new ToolItem("SteelAxe", 0));
3296                                         found = true;
3297                                 }
3298                         }
3299
3300                         // Chest
3301                         if(!found)
3302                         {
3303                                 ItemSpec specs[9];
3304                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3305                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3306                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3307                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3308                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3309                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3310                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3311                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3312                                 if(checkItemCombination(items, specs))
3313                                 {
3314                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3315                                         found = true;
3316                                 }
3317                         }
3318
3319                         // Furnace
3320                         if(!found)
3321                         {
3322                                 ItemSpec specs[9];
3323                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3324                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3325                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3326                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3327                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3328                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3329                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3330                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3331                                 if(checkItemCombination(items, specs))
3332                                 {
3333                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3334                                         found = true;
3335                                 }
3336                         }
3337
3338                         // Steel block
3339                         if(!found)
3340                         {
3341                                 ItemSpec specs[9];
3342                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3343                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3344                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3345                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3346                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3347                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3348                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3349                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3350                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3351                                 if(checkItemCombination(items, specs))
3352                                 {
3353                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3354                                         found = true;
3355                                 }
3356                         }
3357
3358                 }
3359         } // if creative_mode == false
3360
3361         /*
3362                 Serialize it
3363         */
3364
3365         std::ostringstream os;
3366         //os.imbue(std::locale("C"));
3367
3368         player->inventory.serialize(os);
3369
3370         std::string s = os.str();
3371         
3372         SharedBuffer<u8> data(s.size()+2);
3373         writeU16(&data[0], TOCLIENT_INVENTORY);
3374         memcpy(&data[2], s.c_str(), s.size());
3375         
3376         // Send as reliable
3377         m_con.Send(peer_id, 0, data, true);
3378 }
3379
3380 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3381 {
3382         DSTACK(__FUNCTION_NAME);
3383         
3384         std::ostringstream os(std::ios_base::binary);
3385         u8 buf[12];
3386         
3387         // Write command
3388         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3389         os.write((char*)buf, 2);
3390         
3391         // Write length
3392         writeU16(buf, message.size());
3393         os.write((char*)buf, 2);
3394         
3395         // Write string
3396         for(u32 i=0; i<message.size(); i++)
3397         {
3398                 u16 w = message[i];
3399                 writeU16(buf, w);
3400                 os.write((char*)buf, 2);
3401         }
3402         
3403         // Make data buffer
3404         std::string s = os.str();
3405         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3406         // Send as reliable
3407         m_con.Send(peer_id, 0, data, true);
3408 }
3409
3410 void Server::BroadcastChatMessage(const std::wstring &message)
3411 {
3412         for(core::map<u16, RemoteClient*>::Iterator
3413                 i = m_clients.getIterator();
3414                 i.atEnd() == false; i++)
3415         {
3416                 // Get client and check that it is valid
3417                 RemoteClient *client = i.getNode()->getValue();
3418                 assert(client->peer_id == i.getNode()->getKey());
3419                 if(client->serialization_version == SER_FMT_VER_INVALID)
3420                         continue;
3421
3422                 SendChatMessage(client->peer_id, message);
3423         }
3424 }
3425
3426 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3427         core::list<u16> *far_players, float far_d_nodes)
3428 {
3429         float maxd = far_d_nodes*BS;
3430         v3f p_f = intToFloat(p, BS);
3431
3432         // Create packet
3433         u32 replysize = 8;
3434         SharedBuffer<u8> reply(replysize);
3435         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3436         writeS16(&reply[2], p.X);
3437         writeS16(&reply[4], p.Y);
3438         writeS16(&reply[6], p.Z);
3439
3440         for(core::map<u16, RemoteClient*>::Iterator
3441                 i = m_clients.getIterator();
3442                 i.atEnd() == false; i++)
3443         {
3444                 // Get client and check that it is valid
3445                 RemoteClient *client = i.getNode()->getValue();
3446                 assert(client->peer_id == i.getNode()->getKey());
3447                 if(client->serialization_version == SER_FMT_VER_INVALID)
3448                         continue;
3449
3450                 // Don't send if it's the same one
3451                 if(client->peer_id == ignore_id)
3452                         continue;
3453                 
3454                 if(far_players)
3455                 {
3456                         // Get player
3457                         Player *player = m_env.getPlayer(client->peer_id);
3458                         if(player)
3459                         {
3460                                 // If player is far away, only set modified blocks not sent
3461                                 v3f player_pos = player->getPosition();
3462                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3463                                 {
3464                                         far_players->push_back(client->peer_id);
3465                                         continue;
3466                                 }
3467                         }
3468                 }
3469
3470                 // Send as reliable
3471                 m_con.Send(client->peer_id, 0, reply, true);
3472         }
3473 }
3474
3475 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3476                 core::list<u16> *far_players, float far_d_nodes)
3477 {
3478         float maxd = far_d_nodes*BS;
3479         v3f p_f = intToFloat(p, BS);
3480
3481         for(core::map<u16, RemoteClient*>::Iterator
3482                 i = m_clients.getIterator();
3483                 i.atEnd() == false; i++)
3484         {
3485                 // Get client and check that it is valid
3486                 RemoteClient *client = i.getNode()->getValue();
3487                 assert(client->peer_id == i.getNode()->getKey());
3488                 if(client->serialization_version == SER_FMT_VER_INVALID)
3489                         continue;
3490
3491                 // Don't send if it's the same one
3492                 if(client->peer_id == ignore_id)
3493                         continue;
3494
3495                 if(far_players)
3496                 {
3497                         // Get player
3498                         Player *player = m_env.getPlayer(client->peer_id);
3499                         if(player)
3500                         {
3501                                 // If player is far away, only set modified blocks not sent
3502                                 v3f player_pos = player->getPosition();
3503                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3504                                 {
3505                                         far_players->push_back(client->peer_id);
3506                                         continue;
3507                                 }
3508                         }
3509                 }
3510
3511                 // Create packet
3512                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3513                 SharedBuffer<u8> reply(replysize);
3514                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3515                 writeS16(&reply[2], p.X);
3516                 writeS16(&reply[4], p.Y);
3517                 writeS16(&reply[6], p.Z);
3518                 n.serialize(&reply[8], client->serialization_version);
3519
3520                 // Send as reliable
3521                 m_con.Send(client->peer_id, 0, reply, true);
3522         }
3523 }
3524
3525 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3526 {
3527         DSTACK(__FUNCTION_NAME);
3528         /*
3529                 Create a packet with the block in the right format
3530         */
3531         
3532         std::ostringstream os(std::ios_base::binary);
3533         block->serialize(os, ver);
3534         std::string s = os.str();
3535         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3536
3537         u32 replysize = 8 + blockdata.getSize();
3538         SharedBuffer<u8> reply(replysize);
3539         v3s16 p = block->getPos();
3540         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3541         writeS16(&reply[2], p.X);
3542         writeS16(&reply[4], p.Y);
3543         writeS16(&reply[6], p.Z);
3544         memcpy(&reply[8], *blockdata, blockdata.getSize());
3545
3546         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3547                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3548         
3549         /*
3550                 Send packet
3551         */
3552         m_con.Send(peer_id, 1, reply, true);
3553 }
3554
3555 void Server::SendBlocks(float dtime)
3556 {
3557         DSTACK(__FUNCTION_NAME);
3558
3559         JMutexAutoLock envlock(m_env_mutex);
3560         JMutexAutoLock conlock(m_con_mutex);
3561
3562         //TimeTaker timer("Server::SendBlocks");
3563
3564         core::array<PrioritySortedBlockTransfer> queue;
3565
3566         s32 total_sending = 0;
3567
3568         for(core::map<u16, RemoteClient*>::Iterator
3569                 i = m_clients.getIterator();
3570                 i.atEnd() == false; i++)
3571         {
3572                 RemoteClient *client = i.getNode()->getValue();
3573                 assert(client->peer_id == i.getNode()->getKey());
3574
3575                 total_sending += client->SendingCount();
3576                 
3577                 if(client->serialization_version == SER_FMT_VER_INVALID)
3578                         continue;
3579                 
3580                 client->GetNextBlocks(this, dtime, queue);
3581         }
3582
3583         // Sort.
3584         // Lowest priority number comes first.
3585         // Lowest is most important.
3586         queue.sort();
3587
3588         for(u32 i=0; i<queue.size(); i++)
3589         {
3590                 //TODO: Calculate limit dynamically
3591                 if(total_sending >= g_settings.getS32
3592                                 ("max_simultaneous_block_sends_server_total"))
3593                         break;
3594                 
3595                 PrioritySortedBlockTransfer q = queue[i];
3596
3597                 MapBlock *block = NULL;
3598                 try
3599                 {
3600                         block = m_env.getMap().getBlockNoCreate(q.pos);
3601                 }
3602                 catch(InvalidPositionException &e)
3603                 {
3604                         continue;
3605                 }
3606
3607                 RemoteClient *client = getClient(q.peer_id);
3608
3609                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3610
3611                 client->SentBlock(q.pos);
3612
3613                 total_sending++;
3614         }
3615 }
3616
3617
3618 RemoteClient* Server::getClient(u16 peer_id)
3619 {
3620         DSTACK(__FUNCTION_NAME);
3621         //JMutexAutoLock lock(m_con_mutex);
3622         core::map<u16, RemoteClient*>::Node *n;
3623         n = m_clients.find(peer_id);
3624         // A client should exist for all peers
3625         assert(n != NULL);
3626         return n->getValue();
3627 }
3628
3629 std::wstring Server::getStatusString()
3630 {
3631         std::wostringstream os(std::ios_base::binary);
3632         os<<L"# Server: ";
3633         // Uptime
3634         os<<L"uptime="<<m_uptime.get();
3635         // Information about clients
3636         os<<L", clients={";
3637         for(core::map<u16, RemoteClient*>::Iterator
3638                 i = m_clients.getIterator();
3639                 i.atEnd() == false; i++)
3640         {
3641                 // Get client and check that it is valid
3642                 RemoteClient *client = i.getNode()->getValue();
3643                 assert(client->peer_id == i.getNode()->getKey());
3644                 if(client->serialization_version == SER_FMT_VER_INVALID)
3645                         continue;
3646                 // Get player
3647                 Player *player = m_env.getPlayer(client->peer_id);
3648                 // Get name of player
3649                 std::wstring name = L"unknown";
3650                 if(player != NULL)
3651                         name = narrow_to_wide(player->getName());
3652                 // Add name to information string
3653                 os<<name<<L",";
3654         }
3655         os<<L"}";
3656         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3657                 os<<" WARNING: Map saving is disabled."<<std::endl;
3658         return os.str();
3659 }
3660
3661
3662 void setCreativeInventory(Player *player)
3663 {
3664         player->resetInventory();
3665         
3666         // Give some good picks
3667         {
3668                 InventoryItem *item = new ToolItem("STPick", 0);
3669                 void* r = player->inventory.addItem("main", item);
3670                 assert(r == NULL);
3671         }
3672         {
3673                 InventoryItem *item = new ToolItem("MesePick", 0);
3674                 void* r = player->inventory.addItem("main", item);
3675                 assert(r == NULL);
3676         }
3677
3678         /*
3679                 Give materials
3680         */
3681         
3682         // CONTENT_IGNORE-terminated list
3683         u8 material_items[] = {
3684                 CONTENT_TORCH,
3685                 CONTENT_COBBLE,
3686                 CONTENT_MUD,
3687                 CONTENT_STONE,
3688                 CONTENT_SAND,
3689                 CONTENT_TREE,
3690                 CONTENT_LEAVES,
3691                 CONTENT_MESE,
3692                 CONTENT_WATERSOURCE,
3693                 CONTENT_CLOUD,
3694                 CONTENT_CHEST,
3695                 CONTENT_FURNACE,
3696                 CONTENT_SIGN_WALL,
3697                 CONTENT_IGNORE
3698         };
3699         
3700         u8 *mip = material_items;
3701         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3702         {
3703                 if(*mip == CONTENT_IGNORE)
3704                         break;
3705
3706                 InventoryItem *item = new MaterialItem(*mip, 1);
3707                 player->inventory.addItem("main", item);
3708
3709                 mip++;
3710         }
3711
3712 #if 0
3713         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3714         
3715         // add torch first
3716         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3717         player->inventory.addItem("main", item);
3718         
3719         // Then others
3720         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3721         {
3722                 // Skip some materials
3723                 if(i == CONTENT_WATER || i == CONTENT_TORCH
3724                         || i == CONTENT_COALSTONE)
3725                         continue;
3726
3727                 InventoryItem *item = new MaterialItem(i, 1);
3728                 player->inventory.addItem("main", item);
3729         }
3730 #endif
3731
3732         /*// Sign
3733         {
3734                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3735                 void* r = player->inventory.addItem("main", item);
3736                 assert(r == NULL);
3737         }*/
3738 }
3739
3740 Player *Server::emergePlayer(const char *name, const char *password,
3741                 u16 peer_id)
3742 {
3743         /*
3744                 Try to get an existing player
3745         */
3746         Player *player = m_env.getPlayer(name);
3747         if(player != NULL)
3748         {
3749                 // If player is already connected, cancel
3750                 if(player->peer_id != 0)
3751                 {
3752                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
3753                         return NULL;
3754                 }
3755
3756                 // Got one.
3757                 player->peer_id = peer_id;
3758                 
3759                 // Reset inventory to creative if in creative mode
3760                 if(g_settings.getBool("creative_mode"))
3761                 {
3762                         setCreativeInventory(player);
3763                 }
3764
3765                 return player;
3766         }
3767
3768         /*
3769                 If player with the wanted peer_id already exists, cancel.
3770         */
3771         if(m_env.getPlayer(peer_id) != NULL)
3772         {
3773                 dstream<<"emergePlayer(): Player with wrong name but same"
3774                                 " peer_id already exists"<<std::endl;
3775                 return NULL;
3776         }
3777         
3778         /*
3779                 Create a new player
3780         */
3781         {
3782                 player = new ServerRemotePlayer();
3783                 //player->peer_id = c.peer_id;
3784                 //player->peer_id = PEER_ID_INEXISTENT;
3785                 player->peer_id = peer_id;
3786                 player->updateName(name);
3787
3788                 /*
3789                         Set player position
3790                 */
3791                 
3792                 dstream<<"Server: Finding spawn place for player \""
3793                                 <<player->getName()<<"\""<<std::endl;
3794
3795                 v2s16 nodepos;
3796 #if 0
3797                 player->setPosition(intToFloat(v3s16(
3798                                 0,
3799                                 45, //64,
3800                                 0
3801                 ), BS));
3802 #endif
3803 #if 1
3804                 s16 groundheight = 0;
3805 #if 1
3806                 // Try to find a good place a few times
3807                 for(s32 i=0; i<1000; i++)
3808                 {
3809                         s32 range = 1 + i;
3810                         // We're going to try to throw the player to this position
3811                         nodepos = v2s16(-range + (myrand()%(range*2)),
3812                                         -range + (myrand()%(range*2)));
3813                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3814                         // Get sector (NOTE: Don't get because it's slow)
3815                         //m_env.getMap().emergeSector(sectorpos);
3816                         // Get ground height at point (fallbacks to heightmap function)
3817                         groundheight = m_env.getServerMap().findGroundLevel(nodepos);
3818                         // Don't go underwater
3819                         if(groundheight < WATER_LEVEL)
3820                         {
3821                                 //dstream<<"-> Underwater"<<std::endl;
3822                                 continue;
3823                         }
3824                         // Don't go to high places
3825                         if(groundheight > WATER_LEVEL + 4)
3826                         {
3827                                 //dstream<<"-> Underwater"<<std::endl;
3828                                 continue;
3829                         }
3830
3831                         // Found a good place
3832                         dstream<<"Searched through "<<i<<" places."<<std::endl;
3833                         break;
3834                 }
3835 #endif
3836                 
3837                 // If no suitable place was not found, go above water at least.
3838                 if(groundheight < WATER_LEVEL)
3839                         groundheight = WATER_LEVEL;
3840
3841                 player->setPosition(intToFloat(v3s16(
3842                                 nodepos.X,
3843                                 groundheight + 5, // Accomodate mud
3844                                 nodepos.Y
3845                 ), BS));
3846 #endif
3847
3848                 /*
3849                         Add player to environment
3850                 */
3851
3852                 m_env.addPlayer(player);
3853
3854                 /*
3855                         Add stuff to inventory
3856                 */
3857                 
3858                 if(g_settings.getBool("creative_mode"))
3859                 {
3860                         setCreativeInventory(player);
3861                 }
3862                 else
3863                 {
3864                         /*{
3865                                 InventoryItem *item = new ToolItem("WPick", 32000);
3866                                 void* r = player->inventory.addItem("main", item);
3867                                 assert(r == NULL);
3868                         }*/
3869                         /*{
3870                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3871                                 void* r = player->inventory.addItem("main", item);
3872                                 assert(r == NULL);
3873                         }
3874                         {
3875                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3876                                 void* r = player->inventory.addItem("main", item);
3877                                 assert(r == NULL);
3878                         }
3879                         {
3880                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3881                                 void* r = player->inventory.addItem("main", item);
3882                                 assert(r == NULL);
3883                         }
3884                         {
3885                                 InventoryItem *item = new CraftItem("Stick", 4);
3886                                 void* r = player->inventory.addItem("main", item);
3887                                 assert(r == NULL);
3888                         }
3889                         {
3890                                 InventoryItem *item = new ToolItem("WPick", 32000);
3891                                 void* r = player->inventory.addItem("main", item);
3892                                 assert(r == NULL);
3893                         }
3894                         {
3895                                 InventoryItem *item = new ToolItem("STPick", 32000);
3896                                 void* r = player->inventory.addItem("main", item);
3897                                 assert(r == NULL);
3898                         }*/
3899                         /*// Give some lights
3900                         {
3901                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3902                                 bool r = player->inventory.addItem("main", item);
3903                                 assert(r == true);
3904                         }
3905                         // and some signs
3906                         for(u16 i=0; i<4; i++)
3907                         {
3908                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3909                                 bool r = player->inventory.addItem("main", item);
3910                                 assert(r == true);
3911                         }*/
3912                         /*// Give some other stuff
3913                         {
3914                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3915                                 bool r = player->inventory.addItem("main", item);
3916                                 assert(r == true);
3917                         }*/
3918                 }
3919
3920                 return player;
3921                 
3922         } // create new player
3923 }
3924
3925 void Server::handlePeerChange(PeerChange &c)
3926 {
3927         JMutexAutoLock envlock(m_env_mutex);
3928         JMutexAutoLock conlock(m_con_mutex);
3929         
3930         if(c.type == PEER_ADDED)
3931         {
3932                 /*
3933                         Add
3934                 */
3935
3936                 // Error check
3937                 core::map<u16, RemoteClient*>::Node *n;
3938                 n = m_clients.find(c.peer_id);
3939                 // The client shouldn't already exist
3940                 assert(n == NULL);
3941
3942                 // Create client
3943                 RemoteClient *client = new RemoteClient();
3944                 client->peer_id = c.peer_id;
3945                 m_clients.insert(client->peer_id, client);
3946
3947         } // PEER_ADDED
3948         else if(c.type == PEER_REMOVED)
3949         {
3950                 /*
3951                         Delete
3952                 */
3953
3954                 // Error check
3955                 core::map<u16, RemoteClient*>::Node *n;
3956                 n = m_clients.find(c.peer_id);
3957                 // The client should exist
3958                 assert(n != NULL);
3959                 
3960                 // Collect information about leaving in chat
3961                 std::wstring message;
3962                 {
3963                         std::wstring name = L"unknown";
3964                         Player *player = m_env.getPlayer(c.peer_id);
3965                         if(player != NULL)
3966                                 name = narrow_to_wide(player->getName());
3967                         
3968                         message += L"*** ";
3969                         message += name;
3970                         message += L" left game";
3971                         if(c.timeout)
3972                                 message += L" (timed out)";
3973                 }
3974
3975                 /*// Delete player
3976                 {
3977                         m_env.removePlayer(c.peer_id);
3978                 }*/
3979
3980                 // Set player client disconnected
3981                 {
3982                         Player *player = m_env.getPlayer(c.peer_id);
3983                         if(player != NULL)
3984                                 player->peer_id = 0;
3985                 }
3986                 
3987                 // Delete client
3988                 delete m_clients[c.peer_id];
3989                 m_clients.remove(c.peer_id);
3990
3991                 // Send player info to all remaining clients
3992                 SendPlayerInfos();
3993                 
3994                 // Send leave chat message to all remaining clients
3995                 BroadcastChatMessage(message);
3996                 
3997         } // PEER_REMOVED
3998         else
3999         {
4000                 assert(0);
4001         }
4002 }
4003
4004 void Server::handlePeerChanges()
4005 {
4006         while(m_peer_change_queue.size() > 0)
4007         {
4008                 PeerChange c = m_peer_change_queue.pop_front();
4009
4010                 dout_server<<"Server: Handling peer change: "
4011                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4012                                 <<std::endl;
4013
4014                 handlePeerChange(c);
4015         }
4016 }
4017
4018 void dedicated_server_loop(Server &server, bool &kill)
4019 {
4020         DSTACK(__FUNCTION_NAME);
4021         
4022         std::cout<<DTIME<<std::endl;
4023         std::cout<<"========================"<<std::endl;
4024         std::cout<<"Running dedicated server"<<std::endl;
4025         std::cout<<"========================"<<std::endl;
4026         std::cout<<std::endl;
4027
4028         for(;;)
4029         {
4030                 // This is kind of a hack but can be done like this
4031                 // because server.step() is very light
4032                 sleep_ms(30);
4033                 server.step(0.030);
4034
4035                 if(server.getShutdownRequested() || kill)
4036                 {
4037                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4038                         break;
4039                 }
4040
4041                 static int counter = 0;
4042                 counter--;
4043                 if(counter <= 0)
4044                 {
4045                         counter = 10;
4046
4047                         core::list<PlayerInfo> list = server.getPlayerInfo();
4048                         core::list<PlayerInfo>::Iterator i;
4049                         static u32 sum_old = 0;
4050                         u32 sum = PIChecksum(list);
4051                         if(sum != sum_old)
4052                         {
4053                                 std::cout<<DTIME<<"Player info:"<<std::endl;
4054                                 for(i=list.begin(); i!=list.end(); i++)
4055                                 {
4056                                         i->PrintLine(&std::cout);
4057                                 }
4058                         }
4059                         sum_old = sum;
4060                 }
4061         }
4062 }
4063
4064