]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
aba105083134003b7ac48540a855d6b684913997
[dragonfireclient.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         avg_rtt = 0;
896 }
897
898 void PlayerInfo::PrintLine(std::ostream *s)
899 {
900         (*s)<<id<<": ";
901         (*s)<<"\""<<name<<"\" ("
902                         <<(position.X/10)<<","<<(position.Y/10)
903                         <<","<<(position.Z/10)<<") ";
904         address.print(s);
905         (*s)<<" avg_rtt="<<avg_rtt;
906         (*s)<<std::endl;
907 }
908
909 u32 PIChecksum(core::list<PlayerInfo> &l)
910 {
911         core::list<PlayerInfo>::Iterator i;
912         u32 checksum = 1;
913         u32 a = 10;
914         for(i=l.begin(); i!=l.end(); i++)
915         {
916                 checksum += a * (i->id+1);
917                 checksum ^= 0x435aafcd;
918                 a *= 10;
919         }
920         return checksum;
921 }
922
923 /*
924         Server
925 */
926
927 Server::Server(
928                 std::string mapsavedir
929         ):
930         m_env(new ServerMap(mapsavedir), this),
931         m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
932         m_thread(this),
933         m_emergethread(this),
934         m_time_of_day(9000),
935         m_time_counter(0),
936         m_time_of_day_send_timer(0),
937         m_uptime(0),
938         m_mapsavedir(mapsavedir),
939         m_shutdown_requested(false),
940         m_ignore_map_edit_events(false),
941         m_ignore_map_edit_events_peer_id(0)
942 {
943         m_liquid_transform_timer = 0.0;
944         m_print_info_timer = 0.0;
945         m_objectdata_timer = 0.0;
946         m_emergethread_trigger_timer = 0.0;
947         m_savemap_timer = 0.0;
948         
949         m_env_mutex.Init();
950         m_con_mutex.Init();
951         m_step_dtime_mutex.Init();
952         m_step_dtime = 0.0;
953
954         m_env.getMap().addEventReceiver(this);
955
956         // Load players
957         m_env.deSerializePlayers(m_mapsavedir);
958 }
959
960 Server::~Server()
961 {
962         dstream<<"Server::~Server()"<<std::endl;
963
964         /*
965                 Send shutdown message
966         */
967         {
968                 JMutexAutoLock conlock(m_con_mutex);
969                 
970                 std::wstring line = L"*** Server shutting down";
971
972                 /*
973                         Send the message to clients
974                 */
975                 for(core::map<u16, RemoteClient*>::Iterator
976                         i = m_clients.getIterator();
977                         i.atEnd() == false; i++)
978                 {
979                         // Get client and check that it is valid
980                         RemoteClient *client = i.getNode()->getValue();
981                         assert(client->peer_id == i.getNode()->getKey());
982                         if(client->serialization_version == SER_FMT_VER_INVALID)
983                                 continue;
984
985                         try{
986                                 SendChatMessage(client->peer_id, line);
987                         }
988                         catch(con::PeerNotFoundException &e)
989                         {}
990                 }
991         }
992
993         /*
994                 Save players
995         */
996         dstream<<"Server: Saving players"<<std::endl;
997         m_env.serializePlayers(m_mapsavedir);
998         
999         /*
1000                 Stop threads
1001         */
1002         stop();
1003         
1004         /*
1005                 Delete clients
1006         */
1007         {
1008                 JMutexAutoLock clientslock(m_con_mutex);
1009
1010                 for(core::map<u16, RemoteClient*>::Iterator
1011                         i = m_clients.getIterator();
1012                         i.atEnd() == false; i++)
1013                 {
1014                         /*// Delete player
1015                         // NOTE: These are removed by env destructor
1016                         {
1017                                 u16 peer_id = i.getNode()->getKey();
1018                                 JMutexAutoLock envlock(m_env_mutex);
1019                                 m_env.removePlayer(peer_id);
1020                         }*/
1021                         
1022                         // Delete client
1023                         delete i.getNode()->getValue();
1024                 }
1025         }
1026 }
1027
1028 void Server::start(unsigned short port)
1029 {
1030         DSTACK(__FUNCTION_NAME);
1031         // Stop thread if already running
1032         m_thread.stop();
1033         
1034         // Initialize connection
1035         m_con.setTimeoutMs(30);
1036         m_con.Serve(port);
1037
1038         // Start thread
1039         m_thread.setRun(true);
1040         m_thread.Start();
1041         
1042         dout_server<<"Server: Started on port "<<port<<std::endl;
1043 }
1044
1045 void Server::stop()
1046 {
1047         DSTACK(__FUNCTION_NAME);
1048
1049         // Stop threads (set run=false first so both start stopping)
1050         m_thread.setRun(false);
1051         m_emergethread.setRun(false);
1052         m_thread.stop();
1053         m_emergethread.stop();
1054         
1055         dout_server<<"Server: Threads stopped"<<std::endl;
1056 }
1057
1058 void Server::step(float dtime)
1059 {
1060         DSTACK(__FUNCTION_NAME);
1061         // Limit a bit
1062         if(dtime > 2.0)
1063                 dtime = 2.0;
1064         {
1065                 JMutexAutoLock lock(m_step_dtime_mutex);
1066                 m_step_dtime += dtime;
1067         }
1068 }
1069
1070 void Server::AsyncRunStep()
1071 {
1072         DSTACK(__FUNCTION_NAME);
1073         
1074         float dtime;
1075         {
1076                 JMutexAutoLock lock1(m_step_dtime_mutex);
1077                 dtime = m_step_dtime;
1078         }
1079         
1080         // Send blocks to clients
1081         SendBlocks(dtime);
1082         
1083         if(dtime < 0.001)
1084                 return;
1085         
1086         //dstream<<"Server steps "<<dtime<<std::endl;
1087         //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1088         
1089         {
1090                 JMutexAutoLock lock1(m_step_dtime_mutex);
1091                 m_step_dtime -= dtime;
1092         }
1093
1094         /*
1095                 Update uptime
1096         */
1097         {
1098                 m_uptime.set(m_uptime.get() + dtime);
1099         }
1100         
1101         /*
1102                 Update m_time_of_day
1103         */
1104         {
1105                 m_time_counter += dtime;
1106                 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1107                 u32 units = (u32)(m_time_counter*speed);
1108                 m_time_counter -= (f32)units / speed;
1109                 m_time_of_day.set((m_time_of_day.get() + units) % 24000);
1110                 
1111                 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1112
1113                 /*
1114                         Send to clients at constant intervals
1115                 */
1116
1117                 m_time_of_day_send_timer -= dtime;
1118                 if(m_time_of_day_send_timer < 0.0)
1119                 {
1120                         m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1121
1122                         //JMutexAutoLock envlock(m_env_mutex);
1123                         JMutexAutoLock conlock(m_con_mutex);
1124
1125                         for(core::map<u16, RemoteClient*>::Iterator
1126                                 i = m_clients.getIterator();
1127                                 i.atEnd() == false; i++)
1128                         {
1129                                 RemoteClient *client = i.getNode()->getValue();
1130                                 //Player *player = m_env.getPlayer(client->peer_id);
1131                                 
1132                                 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1133                                                 m_time_of_day.get());
1134                                 // Send as reliable
1135                                 m_con.Send(client->peer_id, 0, data, true);
1136                         }
1137                 }
1138         }
1139
1140         {
1141                 // Process connection's timeouts
1142                 JMutexAutoLock lock2(m_con_mutex);
1143                 m_con.RunTimeouts(dtime);
1144         }
1145         
1146         {
1147                 // This has to be called so that the client list gets synced
1148                 // with the peer list of the connection
1149                 handlePeerChanges();
1150         }
1151
1152         {
1153                 // Step environment
1154                 // This also runs Map's timers
1155                 JMutexAutoLock lock(m_env_mutex);
1156                 m_env.step(dtime);
1157         }
1158         
1159         /*
1160                 Do background stuff
1161         */
1162         
1163         /*
1164                 Transform liquids
1165         */
1166         m_liquid_transform_timer += dtime;
1167         if(m_liquid_transform_timer >= 1.00)
1168         {
1169                 m_liquid_transform_timer -= 1.00;
1170                 
1171                 JMutexAutoLock lock(m_env_mutex);
1172                 
1173                 core::map<v3s16, MapBlock*> modified_blocks;
1174                 m_env.getMap().transformLiquids(modified_blocks);
1175 #if 0           
1176                 /*
1177                         Update lighting
1178                 */
1179                 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1180                 ServerMap &map = ((ServerMap&)m_env.getMap());
1181                 map.updateLighting(modified_blocks, lighting_modified_blocks);
1182                 
1183                 // Add blocks modified by lighting to modified_blocks
1184                 for(core::map<v3s16, MapBlock*>::Iterator
1185                                 i = lighting_modified_blocks.getIterator();
1186                                 i.atEnd() == false; i++)
1187                 {
1188                         MapBlock *block = i.getNode()->getValue();
1189                         modified_blocks.insert(block->getPos(), block);
1190                 }
1191 #endif
1192                 /*
1193                         Set the modified blocks unsent for all the clients
1194                 */
1195                 
1196                 JMutexAutoLock lock2(m_con_mutex);
1197
1198                 for(core::map<u16, RemoteClient*>::Iterator
1199                                 i = m_clients.getIterator();
1200                                 i.atEnd() == false; i++)
1201                 {
1202                         RemoteClient *client = i.getNode()->getValue();
1203                         
1204                         if(modified_blocks.size() > 0)
1205                         {
1206                                 // Remove block from sent history
1207                                 client->SetBlocksNotSent(modified_blocks);
1208                         }
1209                 }
1210         }
1211
1212         // Periodically print some info
1213         {
1214                 float &counter = m_print_info_timer;
1215                 counter += dtime;
1216                 if(counter >= 30.0)
1217                 {
1218                         counter = 0.0;
1219
1220                         JMutexAutoLock lock2(m_con_mutex);
1221
1222                         for(core::map<u16, RemoteClient*>::Iterator
1223                                 i = m_clients.getIterator();
1224                                 i.atEnd() == false; i++)
1225                         {
1226                                 //u16 peer_id = i.getNode()->getKey();
1227                                 RemoteClient *client = i.getNode()->getValue();
1228                                 Player *player = m_env.getPlayer(client->peer_id);
1229                                 if(player==NULL)
1230                                         continue;
1231                                 std::cout<<player->getName()<<"\t";
1232                                 client->PrintInfo(std::cout);
1233                         }
1234                 }
1235         }
1236
1237         //if(g_settings.getBool("enable_experimental"))
1238         {
1239
1240         /*
1241                 Check added and deleted active objects
1242         */
1243         {
1244                 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1245
1246                 JMutexAutoLock envlock(m_env_mutex);
1247                 JMutexAutoLock conlock(m_con_mutex);
1248                 
1249                 // Radius inside which objects are active
1250                 s16 radius = 32;
1251
1252                 for(core::map<u16, RemoteClient*>::Iterator
1253                         i = m_clients.getIterator();
1254                         i.atEnd() == false; i++)
1255                 {
1256                         RemoteClient *client = i.getNode()->getValue();
1257                         Player *player = m_env.getPlayer(client->peer_id);
1258                         if(player==NULL)
1259                         {
1260                                 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1261                                                 <<" has no associated player"<<std::endl;
1262                                 continue;
1263                         }
1264                         v3s16 pos = floatToInt(player->getPosition(), BS);
1265
1266                         core::map<u16, bool> removed_objects;
1267                         core::map<u16, bool> added_objects;
1268                         m_env.getRemovedActiveObjects(pos, radius,
1269                                         client->m_known_objects, removed_objects);
1270                         m_env.getAddedActiveObjects(pos, radius,
1271                                         client->m_known_objects, added_objects);
1272                         
1273                         // Ignore if nothing happened
1274                         if(removed_objects.size() == 0 && added_objects.size() == 0)
1275                         {
1276                                 //dstream<<"INFO: active objects: none changed"<<std::endl;
1277                                 continue;
1278                         }
1279                         
1280                         std::string data_buffer;
1281
1282                         char buf[4];
1283                         
1284                         // Handle removed objects
1285                         writeU16((u8*)buf, removed_objects.size());
1286                         data_buffer.append(buf, 2);
1287                         for(core::map<u16, bool>::Iterator
1288                                         i = removed_objects.getIterator();
1289                                         i.atEnd()==false; i++)
1290                         {
1291                                 // Get object
1292                                 u16 id = i.getNode()->getKey();
1293                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1294
1295                                 // Add to data buffer for sending
1296                                 writeU16((u8*)buf, i.getNode()->getKey());
1297                                 data_buffer.append(buf, 2);
1298                                 
1299                                 // Remove from known objects
1300                                 client->m_known_objects.remove(i.getNode()->getKey());
1301
1302                                 if(obj && obj->m_known_by_count > 0)
1303                                         obj->m_known_by_count--;
1304                         }
1305
1306                         // Handle added objects
1307                         writeU16((u8*)buf, added_objects.size());
1308                         data_buffer.append(buf, 2);
1309                         for(core::map<u16, bool>::Iterator
1310                                         i = added_objects.getIterator();
1311                                         i.atEnd()==false; i++)
1312                         {
1313                                 // Get object
1314                                 u16 id = i.getNode()->getKey();
1315                                 ServerActiveObject* obj = m_env.getActiveObject(id);
1316                                 
1317                                 // Get object type
1318                                 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1319                                 if(obj == NULL)
1320                                         dstream<<"WARNING: "<<__FUNCTION_NAME
1321                                                         <<": NULL object"<<std::endl;
1322                                 else
1323                                         type = obj->getType();
1324
1325                                 // Add to data buffer for sending
1326                                 writeU16((u8*)buf, id);
1327                                 data_buffer.append(buf, 2);
1328                                 writeU8((u8*)buf, type);
1329                                 data_buffer.append(buf, 1);
1330                                 
1331                                 if(obj)
1332                                         data_buffer.append(serializeLongString(
1333                                                         obj->getClientInitializationData()));
1334                                 else
1335                                         data_buffer.append(serializeLongString(""));
1336
1337                                 // Add to known objects
1338                                 client->m_known_objects.insert(i.getNode()->getKey(), false);
1339
1340                                 if(obj)
1341                                         obj->m_known_by_count++;
1342                         }
1343
1344                         // Send packet
1345                         SharedBuffer<u8> reply(2 + data_buffer.size());
1346                         writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1347                         memcpy((char*)&reply[2], data_buffer.c_str(),
1348                                         data_buffer.size());
1349                         // Send as reliable
1350                         m_con.Send(client->peer_id, 0, reply, true);
1351
1352                         dstream<<"INFO: Server: Sent object remove/add: "
1353                                         <<removed_objects.size()<<" removed, "
1354                                         <<added_objects.size()<<" added, "
1355                                         <<"packet size is "<<reply.getSize()<<std::endl;
1356                 }
1357
1358 #if 0
1359                 /*
1360                         Collect a list of all the objects known by the clients
1361                         and report it back to the environment.
1362                 */
1363
1364                 core::map<u16, bool> all_known_objects;
1365
1366                 for(core::map<u16, RemoteClient*>::Iterator
1367                         i = m_clients.getIterator();
1368                         i.atEnd() == false; i++)
1369                 {
1370                         RemoteClient *client = i.getNode()->getValue();
1371                         // Go through all known objects of client
1372                         for(core::map<u16, bool>::Iterator
1373                                         i = client->m_known_objects.getIterator();
1374                                         i.atEnd()==false; i++)
1375                         {
1376                                 u16 id = i.getNode()->getKey();
1377                                 all_known_objects[id] = true;
1378                         }
1379                 }
1380                 
1381                 m_env.setKnownActiveObjects(whatever);
1382 #endif
1383
1384         }
1385
1386         /*
1387                 Send object messages
1388         */
1389         {
1390                 JMutexAutoLock envlock(m_env_mutex);
1391                 JMutexAutoLock conlock(m_con_mutex);
1392
1393                 // Key = object id
1394                 // Value = data sent by object
1395                 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1396
1397                 // Get active object messages from environment
1398                 for(;;)
1399                 {
1400                         ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1401                         if(aom.id == 0)
1402                                 break;
1403                         
1404                         core::list<ActiveObjectMessage>* message_list = NULL;
1405                         core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1406                         n = buffered_messages.find(aom.id);
1407                         if(n == NULL)
1408                         {
1409                                 message_list = new core::list<ActiveObjectMessage>;
1410                                 buffered_messages.insert(aom.id, message_list);
1411                         }
1412                         else
1413                         {
1414                                 message_list = n->getValue();
1415                         }
1416                         message_list->push_back(aom);
1417                 }
1418                 
1419                 // Route data to every client
1420                 for(core::map<u16, RemoteClient*>::Iterator
1421                         i = m_clients.getIterator();
1422                         i.atEnd()==false; i++)
1423                 {
1424                         RemoteClient *client = i.getNode()->getValue();
1425                         std::string reliable_data;
1426                         std::string unreliable_data;
1427                         // Go through all objects in message buffer
1428                         for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1429                                         j = buffered_messages.getIterator();
1430                                         j.atEnd()==false; j++)
1431                         {
1432                                 // If object is not known by client, skip it
1433                                 u16 id = j.getNode()->getKey();
1434                                 if(client->m_known_objects.find(id) == NULL)
1435                                         continue;
1436                                 // Get message list of object
1437                                 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1438                                 // Go through every message
1439                                 for(core::list<ActiveObjectMessage>::Iterator
1440                                                 k = list->begin(); k != list->end(); k++)
1441                                 {
1442                                         // Compose the full new data with header
1443                                         ActiveObjectMessage aom = *k;
1444                                         std::string new_data;
1445                                         // Add object id
1446                                         char buf[2];
1447                                         writeU16((u8*)&buf[0], aom.id);
1448                                         new_data.append(buf, 2);
1449                                         // Add data
1450                                         new_data += serializeString(aom.datastring);
1451                                         // Add data to buffer
1452                                         if(aom.reliable)
1453                                                 reliable_data += new_data;
1454                                         else
1455                                                 unreliable_data += new_data;
1456                                 }
1457                         }
1458                         /*
1459                                 reliable_data and unreliable_data are now ready.
1460                                 Send them.
1461                         */
1462                         if(reliable_data.size() > 0)
1463                         {
1464                                 SharedBuffer<u8> reply(2 + reliable_data.size());
1465                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1466                                 memcpy((char*)&reply[2], reliable_data.c_str(),
1467                                                 reliable_data.size());
1468                                 // Send as reliable
1469                                 m_con.Send(client->peer_id, 0, reply, true);
1470                         }
1471                         if(unreliable_data.size() > 0)
1472                         {
1473                                 SharedBuffer<u8> reply(2 + unreliable_data.size());
1474                                 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1475                                 memcpy((char*)&reply[2], unreliable_data.c_str(),
1476                                                 unreliable_data.size());
1477                                 // Send as unreliable
1478                                 m_con.Send(client->peer_id, 0, reply, false);
1479                         }
1480
1481                         /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1482                         {
1483                                 dstream<<"INFO: Server: Size of object message data: "
1484                                                 <<"reliable: "<<reliable_data.size()
1485                                                 <<", unreliable: "<<unreliable_data.size()
1486                                                 <<std::endl;
1487                         }*/
1488                 }
1489
1490                 // Clear buffered_messages
1491                 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1492                                 i = buffered_messages.getIterator();
1493                                 i.atEnd()==false; i++)
1494                 {
1495                         delete i.getNode()->getValue();
1496                 }
1497         }
1498
1499         } // enable_experimental
1500
1501         /*
1502                 Send queued-for-sending map edit events.
1503         */
1504         {
1505                 while(m_unsent_map_edit_queue.size() != 0)
1506                 {
1507                         MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1508
1509                         if(event->type == MEET_ADDNODE)
1510                         {
1511                                 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1512                                 sendAddNode(event->p, event->n, event->already_known_by_peer);
1513                         }
1514                         else if(event->type == MEET_REMOVENODE)
1515                         {
1516                                 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1517                                 sendRemoveNode(event->p, event->already_known_by_peer);
1518                         }
1519                         else if(event->type == MEET_OTHER)
1520                         {
1521                                 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1522                                                 <<std::endl;
1523                         }
1524                         else
1525                         {
1526                                 dstream<<"WARNING: Server: Unknown MapEditEvent "
1527                                                 <<((u32)event->type)<<std::endl;
1528                         }
1529
1530                         delete event;
1531                 }
1532         }
1533
1534         /*
1535                 Send object positions
1536                 TODO: Get rid of MapBlockObjects
1537         */
1538         {
1539                 float &counter = m_objectdata_timer;
1540                 counter += dtime;
1541                 if(counter >= g_settings.getFloat("objectdata_interval"))
1542                 {
1543                         JMutexAutoLock lock1(m_env_mutex);
1544                         JMutexAutoLock lock2(m_con_mutex);
1545                         SendObjectData(counter);
1546
1547                         counter = 0.0;
1548                 }
1549         }
1550         
1551         /*
1552                 Step node metadata
1553         */
1554         {
1555                 //TimeTaker timer("Step node metadata");
1556
1557                 JMutexAutoLock envlock(m_env_mutex);
1558                 JMutexAutoLock conlock(m_con_mutex);
1559                 
1560                 core::map<v3s16, MapBlock*> changed_blocks;
1561                 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1562
1563                 for(core::map<v3s16, MapBlock*>::Iterator
1564                                 i = changed_blocks.getIterator();
1565                                 i.atEnd() == false; i++)
1566                 {
1567                         MapBlock *block = i.getNode()->getValue();
1568
1569                         for(core::map<u16, RemoteClient*>::Iterator
1570                                 i = m_clients.getIterator();
1571                                 i.atEnd()==false; i++)
1572                         {
1573                                 RemoteClient *client = i.getNode()->getValue();
1574                                 client->SetBlockNotSent(block->getPos());
1575                         }
1576                 }
1577         }
1578                 
1579         /*
1580                 Trigger emergethread (it somehow gets to a non-triggered but
1581                 bysy state sometimes)
1582         */
1583         {
1584                 float &counter = m_emergethread_trigger_timer;
1585                 counter += dtime;
1586                 if(counter >= 2.0)
1587                 {
1588                         counter = 0.0;
1589                         
1590                         m_emergethread.trigger();
1591                 }
1592         }
1593
1594         // Save map
1595         {
1596                 float &counter = m_savemap_timer;
1597                 counter += dtime;
1598                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1599                 {
1600                         counter = 0.0;
1601
1602                         JMutexAutoLock lock(m_env_mutex);
1603
1604                         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1605                         {
1606                                 // Save only changed parts
1607                                 m_env.getMap().save(true);
1608
1609                                 // Delete unused sectors
1610                                 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1611                                                 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1612                                 if(deleted_count > 0)
1613                                 {
1614                                         dout_server<<"Server: Unloaded "<<deleted_count
1615                                                         <<" sectors from memory"<<std::endl;
1616                                 }
1617
1618                                 // Save players
1619                                 m_env.serializePlayers(m_mapsavedir);
1620                         }
1621                 }
1622         }
1623 }
1624
1625 void Server::Receive()
1626 {
1627         DSTACK(__FUNCTION_NAME);
1628         u32 data_maxsize = 10000;
1629         Buffer<u8> data(data_maxsize);
1630         u16 peer_id;
1631         u32 datasize;
1632         try{
1633                 {
1634                         JMutexAutoLock conlock(m_con_mutex);
1635                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1636                 }
1637
1638                 // This has to be called so that the client list gets synced
1639                 // with the peer list of the connection
1640                 handlePeerChanges();
1641
1642                 ProcessData(*data, datasize, peer_id);
1643         }
1644         catch(con::InvalidIncomingDataException &e)
1645         {
1646                 derr_server<<"Server::Receive(): "
1647                                 "InvalidIncomingDataException: what()="
1648                                 <<e.what()<<std::endl;
1649         }
1650         catch(con::PeerNotFoundException &e)
1651         {
1652                 //NOTE: This is not needed anymore
1653                 
1654                 // The peer has been disconnected.
1655                 // Find the associated player and remove it.
1656
1657                 /*JMutexAutoLock envlock(m_env_mutex);
1658
1659                 dout_server<<"ServerThread: peer_id="<<peer_id
1660                                 <<" has apparently closed connection. "
1661                                 <<"Removing player."<<std::endl;
1662
1663                 m_env.removePlayer(peer_id);*/
1664         }
1665 }
1666
1667 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1668 {
1669         DSTACK(__FUNCTION_NAME);
1670         // Environment is locked first.
1671         JMutexAutoLock envlock(m_env_mutex);
1672         JMutexAutoLock conlock(m_con_mutex);
1673         
1674         con::Peer *peer;
1675         try{
1676                 peer = m_con.GetPeer(peer_id);
1677         }
1678         catch(con::PeerNotFoundException &e)
1679         {
1680                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1681                                 <<peer_id<<" not found"<<std::endl;
1682                 return;
1683         }
1684         
1685         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1686
1687         try
1688         {
1689
1690         if(datasize < 2)
1691                 return;
1692
1693         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1694         
1695         if(command == TOSERVER_INIT)
1696         {
1697                 // [0] u16 TOSERVER_INIT
1698                 // [2] u8 SER_FMT_VER_HIGHEST
1699                 // [3] u8[20] player_name
1700
1701                 if(datasize < 3)
1702                         return;
1703
1704                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1705                                 <<peer->id<<std::endl;
1706
1707                 // First byte after command is maximum supported
1708                 // serialization version
1709                 u8 client_max = data[2];
1710                 u8 our_max = SER_FMT_VER_HIGHEST;
1711                 // Use the highest version supported by both
1712                 u8 deployed = core::min_(client_max, our_max);
1713                 // If it's lower than the lowest supported, give up.
1714                 if(deployed < SER_FMT_VER_LOWEST)
1715                         deployed = SER_FMT_VER_INVALID;
1716
1717                 //peer->serialization_version = deployed;
1718                 getClient(peer->id)->pending_serialization_version = deployed;
1719
1720                 if(deployed == SER_FMT_VER_INVALID)
1721                 {
1722                         derr_server<<DTIME<<"Server: Cannot negotiate "
1723                                         "serialization version with peer "
1724                                         <<peer_id<<std::endl;
1725                         return;
1726                 }
1727
1728                 /*
1729                         Set up player
1730                 */
1731                 
1732                 // Get player name
1733                 const u32 playername_size = 20;
1734                 char playername[playername_size];
1735                 for(u32 i=0; i<playername_size-1; i++)
1736                 {
1737                         playername[i] = data[3+i];
1738                 }
1739                 playername[playername_size-1] = 0;
1740                 
1741                 // Get player
1742                 Player *player = emergePlayer(playername, "", peer_id);
1743                 //Player *player = m_env.getPlayer(peer_id);
1744
1745                 /*{
1746                         // DEBUG: Test serialization
1747                         std::ostringstream test_os;
1748                         player->serialize(test_os);
1749                         dstream<<"Player serialization test: \""<<test_os.str()
1750                                         <<"\""<<std::endl;
1751                         std::istringstream test_is(test_os.str());
1752                         player->deSerialize(test_is);
1753                 }*/
1754
1755                 // If failed, cancel
1756                 if(player == NULL)
1757                 {
1758                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1759                                         <<": failed to emerge player"<<std::endl;
1760                         return;
1761                 }
1762
1763                 /*
1764                 // If a client is already connected to the player, cancel
1765                 if(player->peer_id != 0)
1766                 {
1767                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1768                                         <<" tried to connect to "
1769                                         "an already connected player (peer_id="
1770                                         <<player->peer_id<<")"<<std::endl;
1771                         return;
1772                 }
1773                 // Set client of player
1774                 player->peer_id = peer_id;
1775                 */
1776
1777                 // Check if player doesn't exist
1778                 if(player == NULL)
1779                         throw con::InvalidIncomingDataException
1780                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1781
1782                 /*// update name if it was supplied
1783                 if(datasize >= 20+3)
1784                 {
1785                         data[20+3-1] = 0;
1786                         player->updateName((const char*)&data[3]);
1787                 }*/
1788                 
1789                 /*
1790                         Answer with a TOCLIENT_INIT
1791                 */
1792                 {
1793                         SharedBuffer<u8> reply(2+1+6+8);
1794                         writeU16(&reply[0], TOCLIENT_INIT);
1795                         writeU8(&reply[2], deployed);
1796                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1797                         //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1798                         writeU64(&reply[2+1+6], 0); // no seed
1799                         
1800                         // Send as reliable
1801                         m_con.Send(peer_id, 0, reply, true);
1802                 }
1803
1804                 /*
1805                         Send complete position information
1806                 */
1807                 SendMovePlayer(player);
1808
1809                 return;
1810         }
1811
1812         if(command == TOSERVER_INIT2)
1813         {
1814                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
1815                                 <<peer->id<<std::endl;
1816
1817
1818                 getClient(peer->id)->serialization_version
1819                                 = getClient(peer->id)->pending_serialization_version;
1820
1821                 /*
1822                         Send some initialization data
1823                 */
1824                 
1825                 // Send player info to all players
1826                 SendPlayerInfos();
1827
1828                 // Send inventory to player
1829                 UpdateCrafting(peer->id);
1830                 SendInventory(peer->id);
1831
1832                 // Send HP
1833                 {
1834                         Player *player = m_env.getPlayer(peer_id);
1835                         SendPlayerHP(player);
1836                 }
1837                 
1838                 // Send time of day
1839                 {
1840                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1841                                         m_time_of_day.get());
1842                         m_con.Send(peer->id, 0, data, true);
1843                 }
1844                 
1845                 // Send information about server to player in chat
1846                 SendChatMessage(peer_id, getStatusString());
1847                 
1848                 // Send information about joining in chat
1849                 {
1850                         std::wstring name = L"unknown";
1851                         Player *player = m_env.getPlayer(peer_id);
1852                         if(player != NULL)
1853                                 name = narrow_to_wide(player->getName());
1854                         
1855                         std::wstring message;
1856                         message += L"*** ";
1857                         message += name;
1858                         message += L" joined game";
1859                         BroadcastChatMessage(message);
1860                 }
1861
1862                 return;
1863         }
1864
1865         if(peer_ser_ver == SER_FMT_VER_INVALID)
1866         {
1867                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
1868                                 " serialization format invalid or not initialized."
1869                                 " Skipping incoming command="<<command<<std::endl;
1870                 return;
1871         }
1872         
1873         Player *player = m_env.getPlayer(peer_id);
1874
1875         if(player == NULL){
1876                 derr_server<<"Server::ProcessData(): Cancelling: "
1877                                 "No player for peer_id="<<peer_id
1878                                 <<std::endl;
1879                 return;
1880         }
1881         if(command == TOSERVER_PLAYERPOS)
1882         {
1883                 if(datasize < 2+12+12+4+4)
1884                         return;
1885         
1886                 u32 start = 0;
1887                 v3s32 ps = readV3S32(&data[start+2]);
1888                 v3s32 ss = readV3S32(&data[start+2+12]);
1889                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
1890                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
1891                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
1892                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
1893                 pitch = wrapDegrees(pitch);
1894                 yaw = wrapDegrees(yaw);
1895                 player->setPosition(position);
1896                 player->setSpeed(speed);
1897                 player->setPitch(pitch);
1898                 player->setYaw(yaw);
1899                 
1900                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
1901                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
1902                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
1903         }
1904         else if(command == TOSERVER_GOTBLOCKS)
1905         {
1906                 if(datasize < 2+1)
1907                         return;
1908                 
1909                 /*
1910                         [0] u16 command
1911                         [2] u8 count
1912                         [3] v3s16 pos_0
1913                         [3+6] v3s16 pos_1
1914                         ...
1915                 */
1916
1917                 u16 count = data[2];
1918                 for(u16 i=0; i<count; i++)
1919                 {
1920                         if((s16)datasize < 2+1+(i+1)*6)
1921                                 throw con::InvalidIncomingDataException
1922                                         ("GOTBLOCKS length is too short");
1923                         v3s16 p = readV3S16(&data[2+1+i*6]);
1924                         /*dstream<<"Server: GOTBLOCKS ("
1925                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1926                         RemoteClient *client = getClient(peer_id);
1927                         client->GotBlock(p);
1928                 }
1929         }
1930         else if(command == TOSERVER_DELETEDBLOCKS)
1931         {
1932                 if(datasize < 2+1)
1933                         return;
1934                 
1935                 /*
1936                         [0] u16 command
1937                         [2] u8 count
1938                         [3] v3s16 pos_0
1939                         [3+6] v3s16 pos_1
1940                         ...
1941                 */
1942
1943                 u16 count = data[2];
1944                 for(u16 i=0; i<count; i++)
1945                 {
1946                         if((s16)datasize < 2+1+(i+1)*6)
1947                                 throw con::InvalidIncomingDataException
1948                                         ("DELETEDBLOCKS length is too short");
1949                         v3s16 p = readV3S16(&data[2+1+i*6]);
1950                         /*dstream<<"Server: DELETEDBLOCKS ("
1951                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1952                         RemoteClient *client = getClient(peer_id);
1953                         client->SetBlockNotSent(p);
1954                 }
1955         }
1956         else if(command == TOSERVER_CLICK_OBJECT)
1957         {
1958                 if(datasize < 13)
1959                         return;
1960
1961                 /*
1962                         [0] u16 command
1963                         [2] u8 button (0=left, 1=right)
1964                         [3] v3s16 block
1965                         [9] s16 id
1966                         [11] u16 item
1967                 */
1968                 u8 button = readU8(&data[2]);
1969                 v3s16 p;
1970                 p.X = readS16(&data[3]);
1971                 p.Y = readS16(&data[5]);
1972                 p.Z = readS16(&data[7]);
1973                 s16 id = readS16(&data[9]);
1974                 //u16 item_i = readU16(&data[11]);
1975
1976                 MapBlock *block = NULL;
1977                 try
1978                 {
1979                         block = m_env.getMap().getBlockNoCreate(p);
1980                 }
1981                 catch(InvalidPositionException &e)
1982                 {
1983                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
1984                         return;
1985                 }
1986
1987                 MapBlockObject *obj = block->getObject(id);
1988
1989                 if(obj == NULL)
1990                 {
1991                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
1992                         return;
1993                 }
1994
1995                 //TODO: Check that object is reasonably close
1996                 
1997                 // Left click
1998                 if(button == 0)
1999                 {
2000                         InventoryList *ilist = player->inventory.getList("main");
2001                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2002                         {
2003                         
2004                                 // Skip if inventory has no free space
2005                                 if(ilist->getUsedSlots() == ilist->getSize())
2006                                 {
2007                                         dout_server<<"Player inventory has no free space"<<std::endl;
2008                                         return;
2009                                 }
2010                                 
2011                                 /*
2012                                         Create the inventory item
2013                                 */
2014                                 InventoryItem *item = NULL;
2015                                 // If it is an item-object, take the item from it
2016                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2017                                 {
2018                                         item = ((ItemObject*)obj)->createInventoryItem();
2019                                 }
2020                                 // Else create an item of the object
2021                                 else
2022                                 {
2023                                         item = new MapBlockObjectItem
2024                                                         (obj->getInventoryString());
2025                                 }
2026                                 
2027                                 // Add to inventory and send inventory
2028                                 ilist->addItem(item);
2029                                 UpdateCrafting(player->peer_id);
2030                                 SendInventory(player->peer_id);
2031                         }
2032
2033                         // Remove from block
2034                         block->removeObject(id);
2035                 }
2036         }
2037         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2038         {
2039                 if(datasize < 7)
2040                         return;
2041
2042                 /*
2043                         length: 7
2044                         [0] u16 command
2045                         [2] u8 button (0=left, 1=right)
2046                         [3] u16 id
2047                         [5] u16 item
2048                 */
2049                 u8 button = readU8(&data[2]);
2050                 u16 id = readS16(&data[3]);
2051                 u16 item_i = readU16(&data[11]);
2052         
2053                 ServerActiveObject *obj = m_env.getActiveObject(id);
2054
2055                 if(obj == NULL)
2056                 {
2057                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2058                                         <<std::endl;
2059                         return;
2060                 }
2061
2062                 //TODO: Check that object is reasonably close
2063                 
2064                 // Left click, pick object up (usually)
2065                 if(button == 0)
2066                 {
2067                         InventoryList *ilist = player->inventory.getList("main");
2068                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2069                         {
2070                         
2071                                 // Skip if inventory has no free space
2072                                 if(ilist->getUsedSlots() == ilist->getSize())
2073                                 {
2074                                         dout_server<<"Player inventory has no free space"<<std::endl;
2075                                         return;
2076                                 }
2077
2078                                 // Skip if object has been removed
2079                                 if(obj->m_removed)
2080                                         return;
2081                                 
2082                                 /*
2083                                         Create the inventory item
2084                                 */
2085                                 InventoryItem *item = obj->createPickedUpItem();
2086                                 
2087                                 if(item)
2088                                 {
2089                                         // Add to inventory and send inventory
2090                                         ilist->addItem(item);
2091                                         UpdateCrafting(player->peer_id);
2092                                         SendInventory(player->peer_id);
2093
2094                                         // Remove object from environment
2095                                         obj->m_removed = true;
2096                                 }
2097                                 else
2098                                 {
2099                                         /*
2100                                                 Item cannot be picked up. Punch it instead.
2101                                         */
2102
2103                                         ToolItem *titem = NULL;
2104                                         std::string toolname = "";
2105
2106                                         InventoryList *mlist = player->inventory.getList("main");
2107                                         if(mlist != NULL)
2108                                         {
2109                                                 InventoryItem *item = mlist->getItem(item_i);
2110                                                 if(item && (std::string)item->getName() == "ToolItem")
2111                                                 {
2112                                                         titem = (ToolItem*)item;
2113                                                         toolname = titem->getToolName();
2114                                                 }
2115                                         }
2116                                         
2117                                         u16 wear = obj->punch(toolname);
2118                                         
2119                                         if(titem)
2120                                         {
2121                                                 bool weared_out = titem->addWear(wear);
2122                                                 if(weared_out)
2123                                                         mlist->deleteItem(item_i);
2124                                                 SendInventory(player->peer_id);
2125                                         }
2126                                 }
2127                         }
2128                 }
2129         }
2130         else if(command == TOSERVER_GROUND_ACTION)
2131         {
2132                 if(datasize < 17)
2133                         return;
2134                 /*
2135                         length: 17
2136                         [0] u16 command
2137                         [2] u8 action
2138                         [3] v3s16 nodepos_undersurface
2139                         [9] v3s16 nodepos_abovesurface
2140                         [15] u16 item
2141                         actions:
2142                         0: start digging
2143                         1: place block
2144                         2: stop digging (all parameters ignored)
2145                         3: digging completed
2146                 */
2147                 u8 action = readU8(&data[2]);
2148                 v3s16 p_under;
2149                 p_under.X = readS16(&data[3]);
2150                 p_under.Y = readS16(&data[5]);
2151                 p_under.Z = readS16(&data[7]);
2152                 v3s16 p_over;
2153                 p_over.X = readS16(&data[9]);
2154                 p_over.Y = readS16(&data[11]);
2155                 p_over.Z = readS16(&data[13]);
2156                 u16 item_i = readU16(&data[15]);
2157
2158                 //TODO: Check that target is reasonably close
2159                 
2160                 /*
2161                         0: start digging
2162                 */
2163                 if(action == 0)
2164                 {
2165                         /*
2166                                 NOTE: This can be used in the future to check if
2167                                 somebody is cheating, by checking the timing.
2168                         */
2169                 } // action == 0
2170
2171                 /*
2172                         2: stop digging
2173                 */
2174                 else if(action == 2)
2175                 {
2176 #if 0
2177                         RemoteClient *client = getClient(peer->id);
2178                         JMutexAutoLock digmutex(client->m_dig_mutex);
2179                         client->m_dig_tool_item = -1;
2180 #endif
2181                 }
2182
2183                 /*
2184                         3: Digging completed
2185                 */
2186                 else if(action == 3)
2187                 {
2188                         // Mandatory parameter; actually used for nothing
2189                         core::map<v3s16, MapBlock*> modified_blocks;
2190
2191                         u8 material;
2192                         u8 mineral = MINERAL_NONE;
2193
2194                         bool cannot_remove_node = false;
2195
2196                         try
2197                         {
2198                                 MapNode n = m_env.getMap().getNode(p_under);
2199                                 // Get mineral
2200                                 mineral = n.getMineral();
2201                                 // Get material at position
2202                                 material = n.d;
2203                                 // If not yet cancelled
2204                                 if(cannot_remove_node == false)
2205                                 {
2206                                         // If it's not diggable, do nothing
2207                                         if(content_diggable(material) == false)
2208                                         {
2209                                                 derr_server<<"Server: Not finishing digging: "
2210                                                                 <<"Node not diggable"
2211                                                                 <<std::endl;
2212                                                 cannot_remove_node = true;
2213                                         }
2214                                 }
2215                                 // If not yet cancelled
2216                                 if(cannot_remove_node == false)
2217                                 {
2218                                         // Get node metadata
2219                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2220                                         if(meta && meta->nodeRemovalDisabled() == true)
2221                                         {
2222                                                 derr_server<<"Server: Not finishing digging: "
2223                                                                 <<"Node metadata disables removal"
2224                                                                 <<std::endl;
2225                                                 cannot_remove_node = true;
2226                                         }
2227                                 }
2228                         }
2229                         catch(InvalidPositionException &e)
2230                         {
2231                                 derr_server<<"Server: Not finishing digging: Node not found."
2232                                                 <<" Adding block to emerge queue."
2233                                                 <<std::endl;
2234                                 m_emerge_queue.addBlock(peer_id,
2235                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2236                                 cannot_remove_node = true;
2237                         }
2238
2239                         /*
2240                                 If node can't be removed, set block to be re-sent to
2241                                 client and quit.
2242                         */
2243                         if(cannot_remove_node)
2244                         {
2245                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2246
2247                                 // Client probably has wrong data.
2248                                 // Set block not sent, so that client will get
2249                                 // a valid one.
2250                                 dstream<<"Client "<<peer_id<<" tried to dig "
2251                                                 <<"node; but node cannot be removed."
2252                                                 <<" setting MapBlock not sent."<<std::endl;
2253                                 RemoteClient *client = getClient(peer_id);
2254                                 v3s16 blockpos = getNodeBlockPos(p_under);
2255                                 client->SetBlockNotSent(blockpos);
2256                                         
2257                                 return;
2258                         }
2259                         
2260                         /*
2261                                 Send the removal to all other clients.
2262                                 - If other player is close, send REMOVENODE
2263                                 - Otherwise set blocks not sent
2264                         */
2265                         core::list<u16> far_players;
2266                         sendRemoveNode(p_under, peer_id, &far_players, 100);
2267                         
2268                         /*
2269                                 Update and send inventory
2270                         */
2271
2272                         if(g_settings.getBool("creative_mode") == false)
2273                         {
2274                                 /*
2275                                         Wear out tool
2276                                 */
2277                                 InventoryList *mlist = player->inventory.getList("main");
2278                                 if(mlist != NULL)
2279                                 {
2280                                         InventoryItem *item = mlist->getItem(item_i);
2281                                         if(item && (std::string)item->getName() == "ToolItem")
2282                                         {
2283                                                 ToolItem *titem = (ToolItem*)item;
2284                                                 std::string toolname = titem->getToolName();
2285
2286                                                 // Get digging properties for material and tool
2287                                                 DiggingProperties prop =
2288                                                                 getDiggingProperties(material, toolname);
2289
2290                                                 if(prop.diggable == false)
2291                                                 {
2292                                                         derr_server<<"Server: WARNING: Player digged"
2293                                                                         <<" with impossible material + tool"
2294                                                                         <<" combination"<<std::endl;
2295                                                 }
2296                                                 
2297                                                 bool weared_out = titem->addWear(prop.wear);
2298
2299                                                 if(weared_out)
2300                                                 {
2301                                                         mlist->deleteItem(item_i);
2302                                                 }
2303                                         }
2304                                 }
2305
2306                                 /*
2307                                         Add dug item to inventory
2308                                 */
2309
2310                                 InventoryItem *item = NULL;
2311
2312                                 if(mineral != MINERAL_NONE)
2313                                         item = getDiggedMineralItem(mineral);
2314                                 
2315                                 // If not mineral
2316                                 if(item == NULL)
2317                                 {
2318                                         std::string &dug_s = content_features(material).dug_item;
2319                                         if(dug_s != "")
2320                                         {
2321                                                 std::istringstream is(dug_s, std::ios::binary);
2322                                                 item = InventoryItem::deSerialize(is);
2323                                         }
2324                                 }
2325                                 
2326                                 if(item != NULL)
2327                                 {
2328                                         // Add a item to inventory
2329                                         player->inventory.addItem("main", item);
2330
2331                                         // Send inventory
2332                                         UpdateCrafting(player->peer_id);
2333                                         SendInventory(player->peer_id);
2334                                 }
2335                         }
2336
2337                         /*
2338                                 Remove the node
2339                                 (this takes some time so it is done after the quick stuff)
2340                         */
2341                         m_ignore_map_edit_events = true;
2342                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2343                         m_ignore_map_edit_events = false;
2344                         
2345                         /*
2346                                 Set blocks not sent to far players
2347                         */
2348                         for(core::list<u16>::Iterator
2349                                         i = far_players.begin();
2350                                         i != far_players.end(); i++)
2351                         {
2352                                 u16 peer_id = *i;
2353                                 RemoteClient *client = getClient(peer_id);
2354                                 if(client==NULL)
2355                                         continue;
2356                                 client->SetBlocksNotSent(modified_blocks);
2357                         }
2358                 }
2359                 
2360                 /*
2361                         1: place block
2362                 */
2363                 else if(action == 1)
2364                 {
2365
2366                         InventoryList *ilist = player->inventory.getList("main");
2367                         if(ilist == NULL)
2368                                 return;
2369
2370                         // Get item
2371                         InventoryItem *item = ilist->getItem(item_i);
2372                         
2373                         // If there is no item, it is not possible to add it anywhere
2374                         if(item == NULL)
2375                                 return;
2376                         
2377                         /*
2378                                 Handle material items
2379                         */
2380                         if(std::string("MaterialItem") == item->getName())
2381                         {
2382                                 try{
2383                                         // Don't add a node if this is not a free space
2384                                         MapNode n2 = m_env.getMap().getNode(p_over);
2385                                         if(content_buildable_to(n2.d) == false)
2386                                         {
2387                                                 // Client probably has wrong data.
2388                                                 // Set block not sent, so that client will get
2389                                                 // a valid one.
2390                                                 dstream<<"Client "<<peer_id<<" tried to place"
2391                                                                 <<" node in invalid position; setting"
2392                                                                 <<" MapBlock not sent."<<std::endl;
2393                                                 RemoteClient *client = getClient(peer_id);
2394                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2395                                                 client->SetBlockNotSent(blockpos);
2396                                                 return;
2397                                         }
2398                                 }
2399                                 catch(InvalidPositionException &e)
2400                                 {
2401                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2402                                                         <<" Adding block to emerge queue."
2403                                                         <<std::endl;
2404                                         m_emerge_queue.addBlock(peer_id,
2405                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2406                                         return;
2407                                 }
2408
2409                                 // Reset build time counter
2410                                 getClient(peer->id)->m_time_from_building = 0.0;
2411                                 
2412                                 // Create node data
2413                                 MaterialItem *mitem = (MaterialItem*)item;
2414                                 MapNode n;
2415                                 n.d = mitem->getMaterial();
2416                                 if(content_features(n.d).wall_mounted)
2417                                         n.dir = packDir(p_under - p_over);
2418                                 
2419                                 /*
2420                                         Send to all players
2421                                 */
2422                                 core::list<u16> far_players;
2423                                 sendAddNode(p_over, n, 0, &far_players, 100);
2424                                 
2425                                 /*
2426                                         Handle inventory
2427                                 */
2428                                 InventoryList *ilist = player->inventory.getList("main");
2429                                 if(g_settings.getBool("creative_mode") == false && ilist)
2430                                 {
2431                                         // Remove from inventory and send inventory
2432                                         if(mitem->getCount() == 1)
2433                                                 ilist->deleteItem(item_i);
2434                                         else
2435                                                 mitem->remove(1);
2436                                         // Send inventory
2437                                         UpdateCrafting(peer_id);
2438                                         SendInventory(peer_id);
2439                                 }
2440                                 
2441                                 /*
2442                                         Add node.
2443
2444                                         This takes some time so it is done after the quick stuff
2445                                 */
2446                                 core::map<v3s16, MapBlock*> modified_blocks;
2447                                 m_ignore_map_edit_events = true;
2448                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2449                                 m_ignore_map_edit_events = false;
2450                                 
2451                                 /*
2452                                         Set blocks not sent to far players
2453                                 */
2454                                 for(core::list<u16>::Iterator
2455                                                 i = far_players.begin();
2456                                                 i != far_players.end(); i++)
2457                                 {
2458                                         u16 peer_id = *i;
2459                                         RemoteClient *client = getClient(peer_id);
2460                                         if(client==NULL)
2461                                                 continue;
2462                                         client->SetBlocksNotSent(modified_blocks);
2463                                 }
2464
2465                                 /*
2466                                         Calculate special events
2467                                 */
2468                                 
2469                                 /*if(n.d == CONTENT_MESE)
2470                                 {
2471                                         u32 count = 0;
2472                                         for(s16 z=-1; z<=1; z++)
2473                                         for(s16 y=-1; y<=1; y++)
2474                                         for(s16 x=-1; x<=1; x++)
2475                                         {
2476                                                 
2477                                         }
2478                                 }*/
2479                         }
2480                         /*
2481                                 Place other item (not a block)
2482                         */
2483                         else
2484                         {
2485                                 v3s16 blockpos = getNodeBlockPos(p_over);
2486                                 
2487                                 /*
2488                                         Check that the block is loaded so that the item
2489                                         can properly be added to the static list too
2490                                 */
2491                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2492                                 if(block==NULL)
2493                                 {
2494                                         derr_server<<"Error while placing object: "
2495                                                         "block not found"<<std::endl;
2496                                         return;
2497                                 }
2498
2499                                 dout_server<<"Placing a miscellaneous item on map"
2500                                                 <<std::endl;
2501                                 
2502                                 // Calculate a position for it
2503                                 v3f pos = intToFloat(p_over, BS);
2504                                 //pos.Y -= BS*0.45;
2505                                 pos.Y -= BS*0.25; // let it drop a bit
2506                                 // Randomize a bit
2507                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2508                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2509
2510                                 /*
2511                                         Create the object
2512                                 */
2513                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2514
2515                                 if(obj == NULL)
2516                                 {
2517                                         derr_server<<"WARNING: item resulted in NULL object, "
2518                                                         <<"not placing onto map"
2519                                                         <<std::endl;
2520                                 }
2521                                 else
2522                                 {
2523                                         // Add the object to the environment
2524                                         m_env.addActiveObject(obj);
2525                                         
2526                                         dout_server<<"Placed object"<<std::endl;
2527
2528                                         if(g_settings.getBool("creative_mode") == false)
2529                                         {
2530                                                 // Delete the right amount of items from the slot
2531                                                 u16 dropcount = item->getDropCount();
2532                                                 
2533                                                 // Delete item if all gone
2534                                                 if(item->getCount() <= dropcount)
2535                                                 {
2536                                                         if(item->getCount() < dropcount)
2537                                                                 dstream<<"WARNING: Server: dropped more items"
2538                                                                                 <<" than the slot contains"<<std::endl;
2539                                                         
2540                                                         InventoryList *ilist = player->inventory.getList("main");
2541                                                         if(ilist)
2542                                                                 // Remove from inventory and send inventory
2543                                                                 ilist->deleteItem(item_i);
2544                                                 }
2545                                                 // Else decrement it
2546                                                 else
2547                                                         item->remove(dropcount);
2548                                                 
2549                                                 // Send inventory
2550                                                 UpdateCrafting(peer_id);
2551                                                 SendInventory(peer_id);
2552                                         }
2553                                 }
2554                         }
2555
2556                 } // action == 1
2557
2558                 /*
2559                         Catch invalid actions
2560                 */
2561                 else
2562                 {
2563                         derr_server<<"WARNING: Server: Invalid action "
2564                                         <<action<<std::endl;
2565                 }
2566         }
2567 #if 0
2568         else if(command == TOSERVER_RELEASE)
2569         {
2570                 if(datasize < 3)
2571                         return;
2572                 /*
2573                         length: 3
2574                         [0] u16 command
2575                         [2] u8 button
2576                 */
2577                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2578         }
2579 #endif
2580         else if(command == TOSERVER_SIGNTEXT)
2581         {
2582                 /*
2583                         u16 command
2584                         v3s16 blockpos
2585                         s16 id
2586                         u16 textlen
2587                         textdata
2588                 */
2589                 std::string datastring((char*)&data[2], datasize-2);
2590                 std::istringstream is(datastring, std::ios_base::binary);
2591                 u8 buf[6];
2592                 // Read stuff
2593                 is.read((char*)buf, 6);
2594                 v3s16 blockpos = readV3S16(buf);
2595                 is.read((char*)buf, 2);
2596                 s16 id = readS16(buf);
2597                 is.read((char*)buf, 2);
2598                 u16 textlen = readU16(buf);
2599                 std::string text;
2600                 for(u16 i=0; i<textlen; i++)
2601                 {
2602                         is.read((char*)buf, 1);
2603                         text += (char)buf[0];
2604                 }
2605
2606                 MapBlock *block = NULL;
2607                 try
2608                 {
2609                         block = m_env.getMap().getBlockNoCreate(blockpos);
2610                 }
2611                 catch(InvalidPositionException &e)
2612                 {
2613                         derr_server<<"Error while setting sign text: "
2614                                         "block not found"<<std::endl;
2615                         return;
2616                 }
2617
2618                 MapBlockObject *obj = block->getObject(id);
2619                 if(obj == NULL)
2620                 {
2621                         derr_server<<"Error while setting sign text: "
2622                                         "object not found"<<std::endl;
2623                         return;
2624                 }
2625                 
2626                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2627                 {
2628                         derr_server<<"Error while setting sign text: "
2629                                         "object is not a sign"<<std::endl;
2630                         return;
2631                 }
2632
2633                 ((SignObject*)obj)->setText(text);
2634
2635                 obj->getBlock()->setChangedFlag();
2636         }
2637         else if(command == TOSERVER_SIGNNODETEXT)
2638         {
2639                 /*
2640                         u16 command
2641                         v3s16 p
2642                         u16 textlen
2643                         textdata
2644                 */
2645                 std::string datastring((char*)&data[2], datasize-2);
2646                 std::istringstream is(datastring, std::ios_base::binary);
2647                 u8 buf[6];
2648                 // Read stuff
2649                 is.read((char*)buf, 6);
2650                 v3s16 p = readV3S16(buf);
2651                 is.read((char*)buf, 2);
2652                 u16 textlen = readU16(buf);
2653                 std::string text;
2654                 for(u16 i=0; i<textlen; i++)
2655                 {
2656                         is.read((char*)buf, 1);
2657                         text += (char)buf[0];
2658                 }
2659
2660                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2661                 if(!meta)
2662                         return;
2663                 if(meta->typeId() != CONTENT_SIGN_WALL)
2664                         return;
2665                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2666                 signmeta->setText(text);
2667                 
2668                 v3s16 blockpos = getNodeBlockPos(p);
2669                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2670                 if(block)
2671                 {
2672                         block->setChangedFlag();
2673                 }
2674
2675                 for(core::map<u16, RemoteClient*>::Iterator
2676                         i = m_clients.getIterator();
2677                         i.atEnd()==false; i++)
2678                 {
2679                         RemoteClient *client = i.getNode()->getValue();
2680                         client->SetBlockNotSent(blockpos);
2681                 }
2682         }
2683         else if(command == TOSERVER_INVENTORY_ACTION)
2684         {
2685                 /*// Ignore inventory changes if in creative mode
2686                 if(g_settings.getBool("creative_mode") == true)
2687                 {
2688                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2689                                         <<std::endl;
2690                         return;
2691                 }*/
2692                 // Strip command and create a stream
2693                 std::string datastring((char*)&data[2], datasize-2);
2694                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2695                 std::istringstream is(datastring, std::ios_base::binary);
2696                 // Create an action
2697                 InventoryAction *a = InventoryAction::deSerialize(is);
2698                 if(a != NULL)
2699                 {
2700                         // Create context
2701                         InventoryContext c;
2702                         c.current_player = player;
2703
2704                         /*
2705                                 Handle craftresult specially if not in creative mode
2706                         */
2707                         bool disable_action = false;
2708                         if(a->getType() == IACTION_MOVE
2709                                         && g_settings.getBool("creative_mode") == false)
2710                         {
2711                                 IMoveAction *ma = (IMoveAction*)a;
2712                                 if(ma->to_inv == "current_player" &&
2713                                                 ma->from_inv == "current_player")
2714                                 {
2715                                         InventoryList *rlist = player->inventory.getList("craftresult");
2716                                         assert(rlist);
2717                                         InventoryList *clist = player->inventory.getList("craft");
2718                                         assert(clist);
2719                                         InventoryList *mlist = player->inventory.getList("main");
2720                                         assert(mlist);
2721                                         /*
2722                                                 Craftresult is no longer preview if something
2723                                                 is moved into it
2724                                         */
2725                                         if(ma->to_list == "craftresult"
2726                                                         && ma->from_list != "craftresult")
2727                                         {
2728                                                 // If it currently is a preview, remove
2729                                                 // its contents
2730                                                 if(player->craftresult_is_preview)
2731                                                 {
2732                                                         rlist->deleteItem(0);
2733                                                 }
2734                                                 player->craftresult_is_preview = false;
2735                                         }
2736                                         /*
2737                                                 Crafting takes place if this condition is true.
2738                                         */
2739                                         if(player->craftresult_is_preview &&
2740                                                         ma->from_list == "craftresult")
2741                                         {
2742                                                 player->craftresult_is_preview = false;
2743                                                 clist->decrementMaterials(1);
2744                                         }
2745                                         /*
2746                                                 If the craftresult is placed on itself, move it to
2747                                                 main inventory instead of doing the action
2748                                         */
2749                                         if(ma->to_list == "craftresult"
2750                                                         && ma->from_list == "craftresult")
2751                                         {
2752                                                 disable_action = true;
2753                                                 
2754                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2755                                                 mlist->addItem(item1);
2756                                         }
2757                                 }
2758                         }
2759                         
2760                         if(disable_action == false)
2761                         {
2762                                 // Feed action to player inventory
2763                                 a->apply(&c, this);
2764                                 // Eat the action
2765                                 delete a;
2766                         }
2767                         else
2768                         {
2769                                 // Send inventory
2770                                 UpdateCrafting(player->peer_id);
2771                                 SendInventory(player->peer_id);
2772                         }
2773                 }
2774                 else
2775                 {
2776                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2777                                         <<"InventoryAction::deSerialize() returned NULL"
2778                                         <<std::endl;
2779                 }
2780         }
2781         else if(command == TOSERVER_CHAT_MESSAGE)
2782         {
2783                 /*
2784                         u16 command
2785                         u16 length
2786                         wstring message
2787                 */
2788                 u8 buf[6];
2789                 std::string datastring((char*)&data[2], datasize-2);
2790                 std::istringstream is(datastring, std::ios_base::binary);
2791                 
2792                 // Read stuff
2793                 is.read((char*)buf, 2);
2794                 u16 len = readU16(buf);
2795                 
2796                 std::wstring message;
2797                 for(u16 i=0; i<len; i++)
2798                 {
2799                         is.read((char*)buf, 2);
2800                         message += (wchar_t)readU16(buf);
2801                 }
2802
2803                 // Get player name of this client
2804                 std::wstring name = narrow_to_wide(player->getName());
2805                 
2806                 // Line to send to players
2807                 std::wstring line;
2808                 // Whether to send to the player that sent the line
2809                 bool send_to_sender = false;
2810                 // Whether to send to other players
2811                 bool send_to_others = false;
2812                 
2813                 // Parse commands
2814                 std::wstring commandprefix = L"/#";
2815                 if(message.substr(0, commandprefix.size()) == commandprefix)
2816                 {
2817                         line += L"Server: ";
2818
2819                         message = message.substr(commandprefix.size());
2820                         // Get player name as narrow string
2821                         std::string name_s = player->getName();
2822                         // Convert message to narrow string
2823                         std::string message_s = wide_to_narrow(message);
2824                         // Operator is the single name defined in config.
2825                         std::string operator_name = g_settings.get("name");
2826                         bool is_operator = (operator_name != "" &&
2827                                         wide_to_narrow(name) == operator_name);
2828                         bool valid_command = false;
2829                         if(message_s == "help")
2830                         {
2831                                 line += L"-!- Available commands: ";
2832                                 line += L"status ";
2833                                 if(is_operator)
2834                                 {
2835                                         line += L"shutdown setting ";
2836                                 }
2837                                 else
2838                                 {
2839                                 }
2840                                 send_to_sender = true;
2841                                 valid_command = true;
2842                         }
2843                         else if(message_s == "status")
2844                         {
2845                                 line = getStatusString();
2846                                 send_to_sender = true;
2847                                 valid_command = true;
2848                         }
2849                         else if(is_operator)
2850                         {
2851                                 if(message_s == "shutdown")
2852                                 {
2853                                         dstream<<DTIME<<" Server: Operator requested shutdown."
2854                                                         <<std::endl;
2855                                         m_shutdown_requested.set(true);
2856                                         
2857                                         line += L"*** Server shutting down (operator request)";
2858                                         send_to_sender = true;
2859                                         valid_command = true;
2860                                 }
2861                                 else if(message_s.substr(0,8) == "setting ")
2862                                 {
2863                                         std::string confline = message_s.substr(8);
2864                                         g_settings.parseConfigLine(confline);
2865                                         line += L"-!- Setting changed.";
2866                                         send_to_sender = true;
2867                                         valid_command = true;
2868                                 }
2869                         }
2870                         
2871                         if(valid_command == false)
2872                         {
2873                                 line += L"-!- Invalid command: " + message;
2874                                 send_to_sender = true;
2875                         }
2876                 }
2877                 else
2878                 {
2879                         line += L"<";
2880                         /*if(is_operator)
2881                                 line += L"@";*/
2882                         line += name;
2883                         line += L"> ";
2884                         line += message;
2885                         send_to_others = true;
2886                 }
2887                 
2888                 if(line != L"")
2889                 {
2890                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2891
2892                         /*
2893                                 Send the message to clients
2894                         */
2895                         for(core::map<u16, RemoteClient*>::Iterator
2896                                 i = m_clients.getIterator();
2897                                 i.atEnd() == false; i++)
2898                         {
2899                                 // Get client and check that it is valid
2900                                 RemoteClient *client = i.getNode()->getValue();
2901                                 assert(client->peer_id == i.getNode()->getKey());
2902                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2903                                         continue;
2904
2905                                 // Filter recipient
2906                                 bool sender_selected = (peer_id == client->peer_id);
2907                                 if(sender_selected == true && send_to_sender == false)
2908                                         continue;
2909                                 if(sender_selected == false && send_to_others == false)
2910                                         continue;
2911
2912                                 SendChatMessage(client->peer_id, line);
2913                         }
2914                 }
2915         }
2916         else if(command == TOSERVER_DAMAGE)
2917         {
2918                 if(g_settings.getBool("enable_damage"))
2919                 {
2920                         std::string datastring((char*)&data[2], datasize-2);
2921                         std::istringstream is(datastring, std::ios_base::binary);
2922                         u8 damage = readU8(is);
2923                         if(player->hp > damage)
2924                         {
2925                                 player->hp -= damage;
2926                         }
2927                         else
2928                         {
2929                                 player->hp = 0;
2930
2931                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2932                                                 <<std::endl;
2933                                 
2934                                 v3f pos = findSpawnPos(m_env.getServerMap());
2935                                 player->setPosition(pos);
2936                                 player->hp = 20;
2937                                 SendMovePlayer(player);
2938                                 SendPlayerHP(player);
2939                                 
2940                                 //TODO: Throw items around
2941                         }
2942                 }
2943
2944                 SendPlayerHP(player);
2945         }
2946         else
2947         {
2948                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2949                                 "unknown command "<<command<<std::endl;
2950         }
2951         
2952         } //try
2953         catch(SendFailedException &e)
2954         {
2955                 derr_server<<"Server::ProcessData(): SendFailedException: "
2956                                 <<"what="<<e.what()
2957                                 <<std::endl;
2958         }
2959 }
2960
2961 void Server::onMapEditEvent(MapEditEvent *event)
2962 {
2963         dstream<<"Server::onMapEditEvent()"<<std::endl;
2964         if(m_ignore_map_edit_events)
2965                 return;
2966         MapEditEvent *e = event->clone();
2967         m_unsent_map_edit_queue.push_back(e);
2968 }
2969
2970 Inventory* Server::getInventory(InventoryContext *c, std::string id)
2971 {
2972         if(id == "current_player")
2973         {
2974                 assert(c->current_player);
2975                 return &(c->current_player->inventory);
2976         }
2977         
2978         Strfnd fn(id);
2979         std::string id0 = fn.next(":");
2980
2981         if(id0 == "nodemeta")
2982         {
2983                 v3s16 p;
2984                 p.X = stoi(fn.next(","));
2985                 p.Y = stoi(fn.next(","));
2986                 p.Z = stoi(fn.next(","));
2987                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2988                 if(meta)
2989                         return meta->getInventory();
2990                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
2991                                 <<"no metadata found"<<std::endl;
2992                 return NULL;
2993         }
2994
2995         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
2996         return NULL;
2997 }
2998 void Server::inventoryModified(InventoryContext *c, std::string id)
2999 {
3000         if(id == "current_player")
3001         {
3002                 assert(c->current_player);
3003                 // Send inventory
3004                 UpdateCrafting(c->current_player->peer_id);
3005                 SendInventory(c->current_player->peer_id);
3006                 return;
3007         }
3008         
3009         Strfnd fn(id);
3010         std::string id0 = fn.next(":");
3011
3012         if(id0 == "nodemeta")
3013         {
3014                 v3s16 p;
3015                 p.X = stoi(fn.next(","));
3016                 p.Y = stoi(fn.next(","));
3017                 p.Z = stoi(fn.next(","));
3018                 v3s16 blockpos = getNodeBlockPos(p);
3019
3020                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3021                 if(meta)
3022                         meta->inventoryModified();
3023
3024                 for(core::map<u16, RemoteClient*>::Iterator
3025                         i = m_clients.getIterator();
3026                         i.atEnd()==false; i++)
3027                 {
3028                         RemoteClient *client = i.getNode()->getValue();
3029                         client->SetBlockNotSent(blockpos);
3030                 }
3031
3032                 return;
3033         }
3034
3035         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3036 }
3037
3038 core::list<PlayerInfo> Server::getPlayerInfo()
3039 {
3040         DSTACK(__FUNCTION_NAME);
3041         JMutexAutoLock envlock(m_env_mutex);
3042         JMutexAutoLock conlock(m_con_mutex);
3043         
3044         core::list<PlayerInfo> list;
3045
3046         core::list<Player*> players = m_env.getPlayers();
3047         
3048         core::list<Player*>::Iterator i;
3049         for(i = players.begin();
3050                         i != players.end(); i++)
3051         {
3052                 PlayerInfo info;
3053
3054                 Player *player = *i;
3055
3056                 try{
3057                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3058                         // Copy info from peer to info struct
3059                         info.id = peer->id;
3060                         info.address = peer->address;
3061                         info.avg_rtt = peer->avg_rtt;
3062                 }
3063                 catch(con::PeerNotFoundException &e)
3064                 {
3065                         // Set dummy peer info
3066                         info.id = 0;
3067                         info.address = Address(0,0,0,0,0);
3068                         info.avg_rtt = 0.0;
3069                 }
3070
3071                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3072                 info.position = player->getPosition();
3073
3074                 list.push_back(info);
3075         }
3076
3077         return list;
3078 }
3079
3080
3081 void Server::peerAdded(con::Peer *peer)
3082 {
3083         DSTACK(__FUNCTION_NAME);
3084         dout_server<<"Server::peerAdded(): peer->id="
3085                         <<peer->id<<std::endl;
3086         
3087         PeerChange c;
3088         c.type = PEER_ADDED;
3089         c.peer_id = peer->id;
3090         c.timeout = false;
3091         m_peer_change_queue.push_back(c);
3092 }
3093
3094 void Server::deletingPeer(con::Peer *peer, bool timeout)
3095 {
3096         DSTACK(__FUNCTION_NAME);
3097         dout_server<<"Server::deletingPeer(): peer->id="
3098                         <<peer->id<<", timeout="<<timeout<<std::endl;
3099         
3100         PeerChange c;
3101         c.type = PEER_REMOVED;
3102         c.peer_id = peer->id;
3103         c.timeout = timeout;
3104         m_peer_change_queue.push_back(c);
3105 }
3106
3107 /*
3108         Static send methods
3109 */
3110
3111 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3112 {
3113         DSTACK(__FUNCTION_NAME);
3114         std::ostringstream os(std::ios_base::binary);
3115
3116         writeU16(os, TOCLIENT_HP);
3117         writeU8(os, hp);
3118
3119         // Make data buffer
3120         std::string s = os.str();
3121         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3122         // Send as reliable
3123         con.Send(peer_id, 0, data, true);
3124 }
3125
3126 /*
3127         Non-static send methods
3128 */
3129
3130 void Server::SendObjectData(float dtime)
3131 {
3132         DSTACK(__FUNCTION_NAME);
3133
3134         core::map<v3s16, bool> stepped_blocks;
3135         
3136         for(core::map<u16, RemoteClient*>::Iterator
3137                 i = m_clients.getIterator();
3138                 i.atEnd() == false; i++)
3139         {
3140                 u16 peer_id = i.getNode()->getKey();
3141                 RemoteClient *client = i.getNode()->getValue();
3142                 assert(client->peer_id == peer_id);
3143                 
3144                 if(client->serialization_version == SER_FMT_VER_INVALID)
3145                         continue;
3146                 
3147                 client->SendObjectData(this, dtime, stepped_blocks);
3148         }
3149 }
3150
3151 void Server::SendPlayerInfos()
3152 {
3153         DSTACK(__FUNCTION_NAME);
3154
3155         //JMutexAutoLock envlock(m_env_mutex);
3156         
3157         // Get connected players
3158         core::list<Player*> players = m_env.getPlayers(true);
3159         
3160         u32 player_count = players.getSize();
3161         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3162
3163         SharedBuffer<u8> data(datasize);
3164         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3165         
3166         u32 start = 2;
3167         core::list<Player*>::Iterator i;
3168         for(i = players.begin();
3169                         i != players.end(); i++)
3170         {
3171                 Player *player = *i;
3172
3173                 /*dstream<<"Server sending player info for player with "
3174                                 "peer_id="<<player->peer_id<<std::endl;*/
3175                 
3176                 writeU16(&data[start], player->peer_id);
3177                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3178                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3179                 start += 2+PLAYERNAME_SIZE;
3180         }
3181
3182         //JMutexAutoLock conlock(m_con_mutex);
3183
3184         // Send as reliable
3185         m_con.SendToAll(0, data, true);
3186 }
3187
3188 void Server::SendInventory(u16 peer_id)
3189 {
3190         DSTACK(__FUNCTION_NAME);
3191         
3192         Player* player = m_env.getPlayer(peer_id);
3193         assert(player);
3194
3195         /*
3196                 Serialize it
3197         */
3198
3199         std::ostringstream os;
3200         //os.imbue(std::locale("C"));
3201
3202         player->inventory.serialize(os);
3203
3204         std::string s = os.str();
3205         
3206         SharedBuffer<u8> data(s.size()+2);
3207         writeU16(&data[0], TOCLIENT_INVENTORY);
3208         memcpy(&data[2], s.c_str(), s.size());
3209         
3210         // Send as reliable
3211         m_con.Send(peer_id, 0, data, true);
3212 }
3213
3214 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3215 {
3216         DSTACK(__FUNCTION_NAME);
3217         
3218         std::ostringstream os(std::ios_base::binary);
3219         u8 buf[12];
3220         
3221         // Write command
3222         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3223         os.write((char*)buf, 2);
3224         
3225         // Write length
3226         writeU16(buf, message.size());
3227         os.write((char*)buf, 2);
3228         
3229         // Write string
3230         for(u32 i=0; i<message.size(); i++)
3231         {
3232                 u16 w = message[i];
3233                 writeU16(buf, w);
3234                 os.write((char*)buf, 2);
3235         }
3236         
3237         // Make data buffer
3238         std::string s = os.str();
3239         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3240         // Send as reliable
3241         m_con.Send(peer_id, 0, data, true);
3242 }
3243
3244 void Server::BroadcastChatMessage(const std::wstring &message)
3245 {
3246         for(core::map<u16, RemoteClient*>::Iterator
3247                 i = m_clients.getIterator();
3248                 i.atEnd() == false; i++)
3249         {
3250                 // Get client and check that it is valid
3251                 RemoteClient *client = i.getNode()->getValue();
3252                 assert(client->peer_id == i.getNode()->getKey());
3253                 if(client->serialization_version == SER_FMT_VER_INVALID)
3254                         continue;
3255
3256                 SendChatMessage(client->peer_id, message);
3257         }
3258 }
3259
3260 void Server::SendPlayerHP(Player *player)
3261 {
3262         SendHP(m_con, player->peer_id, player->hp);
3263 }
3264
3265 void Server::SendMovePlayer(Player *player)
3266 {
3267         DSTACK(__FUNCTION_NAME);
3268         std::ostringstream os(std::ios_base::binary);
3269
3270         writeU16(os, TOCLIENT_MOVE_PLAYER);
3271         writeV3F1000(os, player->getPosition());
3272         writeF1000(os, player->getPitch());
3273         writeF1000(os, player->getYaw());
3274         
3275         {
3276                 v3f pos = player->getPosition();
3277                 f32 pitch = player->getPitch();
3278                 f32 yaw = player->getYaw();
3279                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3280                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3281                                 <<" pitch="<<pitch
3282                                 <<" yaw="<<yaw
3283                                 <<std::endl;
3284         }
3285
3286         // Make data buffer
3287         std::string s = os.str();
3288         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3289         // Send as reliable
3290         m_con.Send(player->peer_id, 0, data, true);
3291 }
3292
3293 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3294         core::list<u16> *far_players, float far_d_nodes)
3295 {
3296         float maxd = far_d_nodes*BS;
3297         v3f p_f = intToFloat(p, BS);
3298
3299         // Create packet
3300         u32 replysize = 8;
3301         SharedBuffer<u8> reply(replysize);
3302         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3303         writeS16(&reply[2], p.X);
3304         writeS16(&reply[4], p.Y);
3305         writeS16(&reply[6], p.Z);
3306
3307         for(core::map<u16, RemoteClient*>::Iterator
3308                 i = m_clients.getIterator();
3309                 i.atEnd() == false; i++)
3310         {
3311                 // Get client and check that it is valid
3312                 RemoteClient *client = i.getNode()->getValue();
3313                 assert(client->peer_id == i.getNode()->getKey());
3314                 if(client->serialization_version == SER_FMT_VER_INVALID)
3315                         continue;
3316
3317                 // Don't send if it's the same one
3318                 if(client->peer_id == ignore_id)
3319                         continue;
3320                 
3321                 if(far_players)
3322                 {
3323                         // Get player
3324                         Player *player = m_env.getPlayer(client->peer_id);
3325                         if(player)
3326                         {
3327                                 // If player is far away, only set modified blocks not sent
3328                                 v3f player_pos = player->getPosition();
3329                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3330                                 {
3331                                         far_players->push_back(client->peer_id);
3332                                         continue;
3333                                 }
3334                         }
3335                 }
3336
3337                 // Send as reliable
3338                 m_con.Send(client->peer_id, 0, reply, true);
3339         }
3340 }
3341
3342 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3343                 core::list<u16> *far_players, float far_d_nodes)
3344 {
3345         float maxd = far_d_nodes*BS;
3346         v3f p_f = intToFloat(p, BS);
3347
3348         for(core::map<u16, RemoteClient*>::Iterator
3349                 i = m_clients.getIterator();
3350                 i.atEnd() == false; i++)
3351         {
3352                 // Get client and check that it is valid
3353                 RemoteClient *client = i.getNode()->getValue();
3354                 assert(client->peer_id == i.getNode()->getKey());
3355                 if(client->serialization_version == SER_FMT_VER_INVALID)
3356                         continue;
3357
3358                 // Don't send if it's the same one
3359                 if(client->peer_id == ignore_id)
3360                         continue;
3361
3362                 if(far_players)
3363                 {
3364                         // Get player
3365                         Player *player = m_env.getPlayer(client->peer_id);
3366                         if(player)
3367                         {
3368                                 // If player is far away, only set modified blocks not sent
3369                                 v3f player_pos = player->getPosition();
3370                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3371                                 {
3372                                         far_players->push_back(client->peer_id);
3373                                         continue;
3374                                 }
3375                         }
3376                 }
3377
3378                 // Create packet
3379                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3380                 SharedBuffer<u8> reply(replysize);
3381                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3382                 writeS16(&reply[2], p.X);
3383                 writeS16(&reply[4], p.Y);
3384                 writeS16(&reply[6], p.Z);
3385                 n.serialize(&reply[8], client->serialization_version);
3386
3387                 // Send as reliable
3388                 m_con.Send(client->peer_id, 0, reply, true);
3389         }
3390 }
3391
3392 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3393 {
3394         DSTACK(__FUNCTION_NAME);
3395         /*
3396                 Create a packet with the block in the right format
3397         */
3398         
3399         std::ostringstream os(std::ios_base::binary);
3400         block->serialize(os, ver);
3401         std::string s = os.str();
3402         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3403
3404         u32 replysize = 8 + blockdata.getSize();
3405         SharedBuffer<u8> reply(replysize);
3406         v3s16 p = block->getPos();
3407         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3408         writeS16(&reply[2], p.X);
3409         writeS16(&reply[4], p.Y);
3410         writeS16(&reply[6], p.Z);
3411         memcpy(&reply[8], *blockdata, blockdata.getSize());
3412
3413         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3414                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3415         
3416         /*
3417                 Send packet
3418         */
3419         m_con.Send(peer_id, 1, reply, true);
3420 }
3421
3422 void Server::SendBlocks(float dtime)
3423 {
3424         DSTACK(__FUNCTION_NAME);
3425
3426         JMutexAutoLock envlock(m_env_mutex);
3427         JMutexAutoLock conlock(m_con_mutex);
3428
3429         //TimeTaker timer("Server::SendBlocks");
3430
3431         core::array<PrioritySortedBlockTransfer> queue;
3432
3433         s32 total_sending = 0;
3434
3435         for(core::map<u16, RemoteClient*>::Iterator
3436                 i = m_clients.getIterator();
3437                 i.atEnd() == false; i++)
3438         {
3439                 RemoteClient *client = i.getNode()->getValue();
3440                 assert(client->peer_id == i.getNode()->getKey());
3441
3442                 total_sending += client->SendingCount();
3443                 
3444                 if(client->serialization_version == SER_FMT_VER_INVALID)
3445                         continue;
3446                 
3447                 client->GetNextBlocks(this, dtime, queue);
3448         }
3449
3450         // Sort.
3451         // Lowest priority number comes first.
3452         // Lowest is most important.
3453         queue.sort();
3454
3455         for(u32 i=0; i<queue.size(); i++)
3456         {
3457                 //TODO: Calculate limit dynamically
3458                 if(total_sending >= g_settings.getS32
3459                                 ("max_simultaneous_block_sends_server_total"))
3460                         break;
3461                 
3462                 PrioritySortedBlockTransfer q = queue[i];
3463
3464                 MapBlock *block = NULL;
3465                 try
3466                 {
3467                         block = m_env.getMap().getBlockNoCreate(q.pos);
3468                 }
3469                 catch(InvalidPositionException &e)
3470                 {
3471                         continue;
3472                 }
3473
3474                 RemoteClient *client = getClient(q.peer_id);
3475
3476                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3477
3478                 client->SentBlock(q.pos);
3479
3480                 total_sending++;
3481         }
3482 }
3483
3484 /*
3485         Something random
3486 */
3487
3488 void Server::UpdateCrafting(u16 peer_id)
3489 {
3490         DSTACK(__FUNCTION_NAME);
3491         
3492         Player* player = m_env.getPlayer(peer_id);
3493         assert(player);
3494
3495         /*
3496                 Calculate crafting stuff
3497         */
3498         if(g_settings.getBool("creative_mode") == false)
3499         {
3500                 InventoryList *clist = player->inventory.getList("craft");
3501                 InventoryList *rlist = player->inventory.getList("craftresult");
3502
3503                 if(rlist->getUsedSlots() == 0)
3504                         player->craftresult_is_preview = true;
3505
3506                 if(rlist && player->craftresult_is_preview)
3507                 {
3508                         rlist->clearItems();
3509                 }
3510                 if(clist && rlist && player->craftresult_is_preview)
3511                 {
3512                         InventoryItem *items[9];
3513                         for(u16 i=0; i<9; i++)
3514                         {
3515                                 items[i] = clist->getItem(i);
3516                         }
3517                         
3518                         bool found = false;
3519
3520                         // Wood
3521                         if(!found)
3522                         {
3523                                 ItemSpec specs[9];
3524                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3525                                 if(checkItemCombination(items, specs))
3526                                 {
3527                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3528                                         found = true;
3529                                 }
3530                         }
3531
3532                         // Stick
3533                         if(!found)
3534                         {
3535                                 ItemSpec specs[9];
3536                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3537                                 if(checkItemCombination(items, specs))
3538                                 {
3539                                         rlist->addItem(new CraftItem("Stick", 4));
3540                                         found = true;
3541                                 }
3542                         }
3543
3544                         // Sign
3545                         if(!found)
3546                         {
3547                                 ItemSpec specs[9];
3548                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3549                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3550                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3551                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3552                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3553                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3554                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3555                                 if(checkItemCombination(items, specs))
3556                                 {
3557                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3558                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3559                                         found = true;
3560                                 }
3561                         }
3562
3563                         // Torch
3564                         if(!found)
3565                         {
3566                                 ItemSpec specs[9];
3567                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3568                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3569                                 if(checkItemCombination(items, specs))
3570                                 {
3571                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3572                                         found = true;
3573                                 }
3574                         }
3575
3576                         // Wooden pick
3577                         if(!found)
3578                         {
3579                                 ItemSpec specs[9];
3580                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3581                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3582                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3583                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3584                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3585                                 if(checkItemCombination(items, specs))
3586                                 {
3587                                         rlist->addItem(new ToolItem("WPick", 0));
3588                                         found = true;
3589                                 }
3590                         }
3591
3592                         // Stone pick
3593                         if(!found)
3594                         {
3595                                 ItemSpec specs[9];
3596                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3597                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3598                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3599                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3600                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3601                                 if(checkItemCombination(items, specs))
3602                                 {
3603                                         rlist->addItem(new ToolItem("STPick", 0));
3604                                         found = true;
3605                                 }
3606                         }
3607
3608                         // Steel pick
3609                         if(!found)
3610                         {
3611                                 ItemSpec specs[9];
3612                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3613                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3614                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3615                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3616                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3617                                 if(checkItemCombination(items, specs))
3618                                 {
3619                                         rlist->addItem(new ToolItem("SteelPick", 0));
3620                                         found = true;
3621                                 }
3622                         }
3623
3624                         // Mese pick
3625                         if(!found)
3626                         {
3627                                 ItemSpec specs[9];
3628                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3629                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3630                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3631                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3632                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3633                                 if(checkItemCombination(items, specs))
3634                                 {
3635                                         rlist->addItem(new ToolItem("MesePick", 0));
3636                                         found = true;
3637                                 }
3638                         }
3639
3640                         // Wooden shovel
3641                         if(!found)
3642                         {
3643                                 ItemSpec specs[9];
3644                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3645                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3646                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3647                                 if(checkItemCombination(items, specs))
3648                                 {
3649                                         rlist->addItem(new ToolItem("WShovel", 0));
3650                                         found = true;
3651                                 }
3652                         }
3653
3654                         // Stone shovel
3655                         if(!found)
3656                         {
3657                                 ItemSpec specs[9];
3658                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3659                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3660                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3661                                 if(checkItemCombination(items, specs))
3662                                 {
3663                                         rlist->addItem(new ToolItem("STShovel", 0));
3664                                         found = true;
3665                                 }
3666                         }
3667
3668                         // Steel shovel
3669                         if(!found)
3670                         {
3671                                 ItemSpec specs[9];
3672                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3673                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3674                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3675                                 if(checkItemCombination(items, specs))
3676                                 {
3677                                         rlist->addItem(new ToolItem("SteelShovel", 0));
3678                                         found = true;
3679                                 }
3680                         }
3681
3682                         // Wooden axe
3683                         if(!found)
3684                         {
3685                                 ItemSpec specs[9];
3686                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3687                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3688                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3689                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3690                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3691                                 if(checkItemCombination(items, specs))
3692                                 {
3693                                         rlist->addItem(new ToolItem("WAxe", 0));
3694                                         found = true;
3695                                 }
3696                         }
3697
3698                         // Stone axe
3699                         if(!found)
3700                         {
3701                                 ItemSpec specs[9];
3702                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3703                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3704                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3705                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3706                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3707                                 if(checkItemCombination(items, specs))
3708                                 {
3709                                         rlist->addItem(new ToolItem("STAxe", 0));
3710                                         found = true;
3711                                 }
3712                         }
3713
3714                         // Steel axe
3715                         if(!found)
3716                         {
3717                                 ItemSpec specs[9];
3718                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3719                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3720                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3721                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3722                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3723                                 if(checkItemCombination(items, specs))
3724                                 {
3725                                         rlist->addItem(new ToolItem("SteelAxe", 0));
3726                                         found = true;
3727                                 }
3728                         }
3729
3730                         // Wooden sword
3731                         if(!found)
3732                         {
3733                                 ItemSpec specs[9];
3734                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3735                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3736                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3737                                 if(checkItemCombination(items, specs))
3738                                 {
3739                                         rlist->addItem(new ToolItem("WSword", 0));
3740                                         found = true;
3741                                 }
3742                         }
3743
3744                         // Stone sword
3745                         if(!found)
3746                         {
3747                                 ItemSpec specs[9];
3748                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3749                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3750                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3751                                 if(checkItemCombination(items, specs))
3752                                 {
3753                                         rlist->addItem(new ToolItem("STSword", 0));
3754                                         found = true;
3755                                 }
3756                         }
3757
3758                         // Steel sword
3759                         if(!found)
3760                         {
3761                                 ItemSpec specs[9];
3762                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3763                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3764                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3765                                 if(checkItemCombination(items, specs))
3766                                 {
3767                                         rlist->addItem(new ToolItem("SteelSword", 0));
3768                                         found = true;
3769                                 }
3770                         }
3771
3772                         // Chest
3773                         if(!found)
3774                         {
3775                                 ItemSpec specs[9];
3776                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3777                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3778                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3779                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3780                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3781                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3782                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3783                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3784                                 if(checkItemCombination(items, specs))
3785                                 {
3786                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3787                                         found = true;
3788                                 }
3789                         }
3790
3791                         // Furnace
3792                         if(!found)
3793                         {
3794                                 ItemSpec specs[9];
3795                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3796                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3797                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3798                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3799                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3800                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3801                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3802                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3803                                 if(checkItemCombination(items, specs))
3804                                 {
3805                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3806                                         found = true;
3807                                 }
3808                         }
3809
3810                         // Steel block
3811                         if(!found)
3812                         {
3813                                 ItemSpec specs[9];
3814                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3815                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3816                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3817                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3818                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3819                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3820                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3821                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3822                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3823                                 if(checkItemCombination(items, specs))
3824                                 {
3825                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3826                                         found = true;
3827                                 }
3828                         }
3829                 }
3830         
3831         } // if creative_mode == false
3832 }
3833
3834 RemoteClient* Server::getClient(u16 peer_id)
3835 {
3836         DSTACK(__FUNCTION_NAME);
3837         //JMutexAutoLock lock(m_con_mutex);
3838         core::map<u16, RemoteClient*>::Node *n;
3839         n = m_clients.find(peer_id);
3840         // A client should exist for all peers
3841         assert(n != NULL);
3842         return n->getValue();
3843 }
3844
3845 std::wstring Server::getStatusString()
3846 {
3847         std::wostringstream os(std::ios_base::binary);
3848         os<<L"# Server: ";
3849         // Version
3850         os<<L"version="<<narrow_to_wide(VERSION_STRING);
3851         // Uptime
3852         os<<L", uptime="<<m_uptime.get();
3853         // Information about clients
3854         os<<L", clients={";
3855         for(core::map<u16, RemoteClient*>::Iterator
3856                 i = m_clients.getIterator();
3857                 i.atEnd() == false; i++)
3858         {
3859                 // Get client and check that it is valid
3860                 RemoteClient *client = i.getNode()->getValue();
3861                 assert(client->peer_id == i.getNode()->getKey());
3862                 if(client->serialization_version == SER_FMT_VER_INVALID)
3863                         continue;
3864                 // Get player
3865                 Player *player = m_env.getPlayer(client->peer_id);
3866                 // Get name of player
3867                 std::wstring name = L"unknown";
3868                 if(player != NULL)
3869                         name = narrow_to_wide(player->getName());
3870                 // Add name to information string
3871                 os<<name<<L",";
3872         }
3873         os<<L"}";
3874         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3875                 os<<" WARNING: Map saving is disabled."<<std::endl;
3876         return os.str();
3877 }
3878
3879
3880 void setCreativeInventory(Player *player)
3881 {
3882         player->resetInventory();
3883         
3884         // Give some good tools
3885         {
3886                 InventoryItem *item = new ToolItem("MesePick", 0);
3887                 void* r = player->inventory.addItem("main", item);
3888                 assert(r == NULL);
3889         }
3890         {
3891                 InventoryItem *item = new ToolItem("SteelPick", 0);
3892                 void* r = player->inventory.addItem("main", item);
3893                 assert(r == NULL);
3894         }
3895         {
3896                 InventoryItem *item = new ToolItem("SteelAxe", 0);
3897                 void* r = player->inventory.addItem("main", item);
3898                 assert(r == NULL);
3899         }
3900         {
3901                 InventoryItem *item = new ToolItem("SteelShovel", 0);
3902                 void* r = player->inventory.addItem("main", item);
3903                 assert(r == NULL);
3904         }
3905
3906         /*
3907                 Give materials
3908         */
3909         
3910         // CONTENT_IGNORE-terminated list
3911         u8 material_items[] = {
3912                 CONTENT_TORCH,
3913                 CONTENT_COBBLE,
3914                 CONTENT_MUD,
3915                 CONTENT_STONE,
3916                 CONTENT_SAND,
3917                 CONTENT_TREE,
3918                 CONTENT_LEAVES,
3919                 CONTENT_MESE,
3920                 CONTENT_WATERSOURCE,
3921                 CONTENT_CLOUD,
3922                 CONTENT_CHEST,
3923                 CONTENT_FURNACE,
3924                 CONTENT_SIGN_WALL,
3925                 CONTENT_IGNORE
3926         };
3927         
3928         u8 *mip = material_items;
3929         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
3930         {
3931                 if(*mip == CONTENT_IGNORE)
3932                         break;
3933
3934                 InventoryItem *item = new MaterialItem(*mip, 1);
3935                 player->inventory.addItem("main", item);
3936
3937                 mip++;
3938         }
3939
3940 #if 0
3941         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3942         
3943         // add torch first
3944         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
3945         player->inventory.addItem("main", item);
3946         
3947         // Then others
3948         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3949         {
3950                 // Skip some materials
3951                 if(i == CONTENT_WATER || i == CONTENT_TORCH
3952                         || i == CONTENT_COALSTONE)
3953                         continue;
3954
3955                 InventoryItem *item = new MaterialItem(i, 1);
3956                 player->inventory.addItem("main", item);
3957         }
3958 #endif
3959
3960         /*// Sign
3961         {
3962                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3963                 void* r = player->inventory.addItem("main", item);
3964                 assert(r == NULL);
3965         }*/
3966 }
3967
3968 v3f findSpawnPos(ServerMap &map)
3969 {
3970         v2s16 nodepos;
3971         s16 groundheight = 0;
3972         
3973         // Try to find a good place a few times
3974         for(s32 i=0; i<1000; i++)
3975         {
3976                 s32 range = 1 + i;
3977                 // We're going to try to throw the player to this position
3978                 nodepos = v2s16(-range + (myrand()%(range*2)),
3979                                 -range + (myrand()%(range*2)));
3980                 v2s16 sectorpos = getNodeSectorPos(nodepos);
3981                 // Get sector (NOTE: Don't get because it's slow)
3982                 //m_env.getMap().emergeSector(sectorpos);
3983                 // Get ground height at point (fallbacks to heightmap function)
3984                 groundheight = map.findGroundLevel(nodepos);
3985                 // Don't go underwater
3986                 if(groundheight < WATER_LEVEL)
3987                 {
3988                         //dstream<<"-> Underwater"<<std::endl;
3989                         continue;
3990                 }
3991                 // Don't go to high places
3992                 if(groundheight > WATER_LEVEL + 4)
3993                 {
3994                         //dstream<<"-> Underwater"<<std::endl;
3995                         continue;
3996                 }
3997
3998                 // Found a good place
3999                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4000                 break;
4001         }
4002         
4003         // If no suitable place was not found, go above water at least.
4004         if(groundheight < WATER_LEVEL)
4005                 groundheight = WATER_LEVEL;
4006
4007         return intToFloat(v3s16(
4008                         nodepos.X,
4009                         groundheight + 2,
4010                         nodepos.Y
4011                         ), BS);
4012 }
4013
4014 Player *Server::emergePlayer(const char *name, const char *password,
4015                 u16 peer_id)
4016 {
4017         /*
4018                 Try to get an existing player
4019         */
4020         Player *player = m_env.getPlayer(name);
4021         if(player != NULL)
4022         {
4023                 // If player is already connected, cancel
4024                 if(player->peer_id != 0)
4025                 {
4026                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4027                         return NULL;
4028                 }
4029
4030                 // Got one.
4031                 player->peer_id = peer_id;
4032                 
4033                 // Reset inventory to creative if in creative mode
4034                 if(g_settings.getBool("creative_mode"))
4035                 {
4036                         setCreativeInventory(player);
4037                 }
4038
4039                 return player;
4040         }
4041
4042         /*
4043                 If player with the wanted peer_id already exists, cancel.
4044         */
4045         if(m_env.getPlayer(peer_id) != NULL)
4046         {
4047                 dstream<<"emergePlayer(): Player with wrong name but same"
4048                                 " peer_id already exists"<<std::endl;
4049                 return NULL;
4050         }
4051         
4052         /*
4053                 Create a new player
4054         */
4055         {
4056                 player = new ServerRemotePlayer();
4057                 //player->peer_id = c.peer_id;
4058                 //player->peer_id = PEER_ID_INEXISTENT;
4059                 player->peer_id = peer_id;
4060                 player->updateName(name);
4061
4062                 /*
4063                         Set player position
4064                 */
4065                 
4066                 dstream<<"Server: Finding spawn place for player \""
4067                                 <<player->getName()<<"\""<<std::endl;
4068
4069                 v3f pos = findSpawnPos(m_env.getServerMap());
4070
4071                 player->setPosition(pos);
4072
4073                 /*
4074                         Add player to environment
4075                 */
4076
4077                 m_env.addPlayer(player);
4078
4079                 /*
4080                         Add stuff to inventory
4081                 */
4082                 
4083                 if(g_settings.getBool("creative_mode"))
4084                 {
4085                         setCreativeInventory(player);
4086                 }
4087                 else if(g_settings.getBool("give_initial_stuff"))
4088                 {
4089                         {
4090                                 InventoryItem *item = new ToolItem("SteelPick", 0);
4091                                 void* r = player->inventory.addItem("main", item);
4092                                 assert(r == NULL);
4093                         }
4094                         {
4095                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4096                                 void* r = player->inventory.addItem("main", item);
4097                                 assert(r == NULL);
4098                         }
4099                         {
4100                                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4101                                 void* r = player->inventory.addItem("main", item);
4102                                 assert(r == NULL);
4103                         }
4104                         {
4105                                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4106                                 void* r = player->inventory.addItem("main", item);
4107                                 assert(r == NULL);
4108                         }
4109                         {
4110                                 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4111                                 void* r = player->inventory.addItem("main", item);
4112                                 assert(r == NULL);
4113                         }
4114                         /*{
4115                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4116                                 void* r = player->inventory.addItem("main", item);
4117                                 assert(r == NULL);
4118                         }
4119                         {
4120                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4121                                 void* r = player->inventory.addItem("main", item);
4122                                 assert(r == NULL);
4123                         }
4124                         {
4125                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4126                                 void* r = player->inventory.addItem("main", item);
4127                                 assert(r == NULL);
4128                         }
4129                         {
4130                                 InventoryItem *item = new CraftItem("Stick", 4);
4131                                 void* r = player->inventory.addItem("main", item);
4132                                 assert(r == NULL);
4133                         }
4134                         {
4135                                 InventoryItem *item = new ToolItem("WPick", 32000);
4136                                 void* r = player->inventory.addItem("main", item);
4137                                 assert(r == NULL);
4138                         }
4139                         {
4140                                 InventoryItem *item = new ToolItem("STPick", 32000);
4141                                 void* r = player->inventory.addItem("main", item);
4142                                 assert(r == NULL);
4143                         }*/
4144                         /*// and some signs
4145                         for(u16 i=0; i<4; i++)
4146                         {
4147                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4148                                 bool r = player->inventory.addItem("main", item);
4149                                 assert(r == true);
4150                         }*/
4151                         /*// Give some other stuff
4152                         {
4153                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4154                                 bool r = player->inventory.addItem("main", item);
4155                                 assert(r == true);
4156                         }*/
4157                 }
4158
4159                 return player;
4160                 
4161         } // create new player
4162 }
4163
4164 void Server::handlePeerChange(PeerChange &c)
4165 {
4166         JMutexAutoLock envlock(m_env_mutex);
4167         JMutexAutoLock conlock(m_con_mutex);
4168         
4169         if(c.type == PEER_ADDED)
4170         {
4171                 /*
4172                         Add
4173                 */
4174
4175                 // Error check
4176                 core::map<u16, RemoteClient*>::Node *n;
4177                 n = m_clients.find(c.peer_id);
4178                 // The client shouldn't already exist
4179                 assert(n == NULL);
4180
4181                 // Create client
4182                 RemoteClient *client = new RemoteClient();
4183                 client->peer_id = c.peer_id;
4184                 m_clients.insert(client->peer_id, client);
4185
4186         } // PEER_ADDED
4187         else if(c.type == PEER_REMOVED)
4188         {
4189                 /*
4190                         Delete
4191                 */
4192
4193                 // Error check
4194                 core::map<u16, RemoteClient*>::Node *n;
4195                 n = m_clients.find(c.peer_id);
4196                 // The client should exist
4197                 assert(n != NULL);
4198                 
4199                 // Collect information about leaving in chat
4200                 std::wstring message;
4201                 {
4202                         std::wstring name = L"unknown";
4203                         Player *player = m_env.getPlayer(c.peer_id);
4204                         if(player != NULL)
4205                                 name = narrow_to_wide(player->getName());
4206                         
4207                         message += L"*** ";
4208                         message += name;
4209                         message += L" left game";
4210                         if(c.timeout)
4211                                 message += L" (timed out)";
4212                 }
4213
4214                 /*// Delete player
4215                 {
4216                         m_env.removePlayer(c.peer_id);
4217                 }*/
4218
4219                 // Set player client disconnected
4220                 {
4221                         Player *player = m_env.getPlayer(c.peer_id);
4222                         if(player != NULL)
4223                                 player->peer_id = 0;
4224                 }
4225                 
4226                 // Delete client
4227                 delete m_clients[c.peer_id];
4228                 m_clients.remove(c.peer_id);
4229
4230                 // Send player info to all remaining clients
4231                 SendPlayerInfos();
4232                 
4233                 // Send leave chat message to all remaining clients
4234                 BroadcastChatMessage(message);
4235                 
4236         } // PEER_REMOVED
4237         else
4238         {
4239                 assert(0);
4240         }
4241 }
4242
4243 void Server::handlePeerChanges()
4244 {
4245         while(m_peer_change_queue.size() > 0)
4246         {
4247                 PeerChange c = m_peer_change_queue.pop_front();
4248
4249                 dout_server<<"Server: Handling peer change: "
4250                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4251                                 <<std::endl;
4252
4253                 handlePeerChange(c);
4254         }
4255 }
4256
4257 void dedicated_server_loop(Server &server, bool &kill)
4258 {
4259         DSTACK(__FUNCTION_NAME);
4260         
4261         std::cout<<DTIME<<std::endl;
4262         std::cout<<"========================"<<std::endl;
4263         std::cout<<"Running dedicated server"<<std::endl;
4264         std::cout<<"========================"<<std::endl;
4265         std::cout<<std::endl;
4266
4267         for(;;)
4268         {
4269                 // This is kind of a hack but can be done like this
4270                 // because server.step() is very light
4271                 sleep_ms(30);
4272                 server.step(0.030);
4273
4274                 if(server.getShutdownRequested() || kill)
4275                 {
4276                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4277                         break;
4278                 }
4279
4280                 static int counter = 0;
4281                 counter--;
4282                 if(counter <= 0)
4283                 {
4284                         counter = 10;
4285
4286                         core::list<PlayerInfo> list = server.getPlayerInfo();
4287                         core::list<PlayerInfo>::Iterator i;
4288                         static u32 sum_old = 0;
4289                         u32 sum = PIChecksum(list);
4290                         if(sum != sum_old)
4291                         {
4292                                 std::cout<<DTIME<<"Player info:"<<std::endl;
4293                                 for(i=list.begin(); i!=list.end(); i++)
4294                                 {
4295                                         i->PrintLine(&std::cout);
4296                                 }
4297                         }
4298                         sum_old = sum;
4299                 }
4300         }
4301 }
4302
4303