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