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