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