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