+ sandstone
[oweals/minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31 #include "config.h"
32 #include "servercommand.h"
33 #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                 // Local player gets all privileges regardless of
2912                 // what's set on their account.
2913                 u64 privs = player->privs;
2914                 if(g_settings.get("name") == player->getName())
2915                         privs = PRIV_ALL;
2916
2917                 // Parse commands
2918                 std::wstring commandprefix = L"/#";
2919                 if(message.substr(0, commandprefix.size()) == commandprefix)
2920                 {
2921                         line += L"Server: ";
2922
2923                         message = message.substr(commandprefix.size());
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                         if(privs & PRIV_SHOUT)
2941                         {
2942                                 line += L"<";
2943                                 line += name;
2944                                 line += L"> ";
2945                                 line += message;
2946                                 send_to_others = true;
2947                         }
2948                         else
2949                         {
2950                                 line += L"Server: You are not allowed to shout";
2951                                 send_to_sender = true;
2952                         }
2953                 }
2954                 
2955                 if(line != L"")
2956                 {
2957                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2958
2959                         /*
2960                                 Send the message to clients
2961                         */
2962                         for(core::map<u16, RemoteClient*>::Iterator
2963                                 i = m_clients.getIterator();
2964                                 i.atEnd() == false; i++)
2965                         {
2966                                 // Get client and check that it is valid
2967                                 RemoteClient *client = i.getNode()->getValue();
2968                                 assert(client->peer_id == i.getNode()->getKey());
2969                                 if(client->serialization_version == SER_FMT_VER_INVALID)
2970                                         continue;
2971
2972                                 // Filter recipient
2973                                 bool sender_selected = (peer_id == client->peer_id);
2974                                 if(sender_selected == true && send_to_sender == false)
2975                                         continue;
2976                                 if(sender_selected == false && send_to_others == false)
2977                                         continue;
2978
2979                                 SendChatMessage(client->peer_id, line);
2980                         }
2981                 }
2982         }
2983         else if(command == TOSERVER_DAMAGE)
2984         {
2985                 if(g_settings.getBool("enable_damage"))
2986                 {
2987                         std::string datastring((char*)&data[2], datasize-2);
2988                         std::istringstream is(datastring, std::ios_base::binary);
2989                         u8 damage = readU8(is);
2990                         if(player->hp > damage)
2991                         {
2992                                 player->hp -= damage;
2993                         }
2994                         else
2995                         {
2996                                 player->hp = 0;
2997
2998                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
2999                                                 <<std::endl;
3000                                 
3001                                 v3f pos = findSpawnPos(m_env.getServerMap());
3002                                 player->setPosition(pos);
3003                                 player->hp = 20;
3004                                 SendMovePlayer(player);
3005                                 SendPlayerHP(player);
3006                                 
3007                                 //TODO: Throw items around
3008                         }
3009                 }
3010
3011                 SendPlayerHP(player);
3012         }
3013         else if(command == TOSERVER_PASSWORD)
3014         {
3015                 /*
3016                         [0] u16 TOSERVER_PASSWORD
3017                         [2] u8[28] old password
3018                         [30] u8[28] new password
3019                 */
3020
3021                 if(datasize != 2+PASSWORD_SIZE*2)
3022                         return;
3023                 char password[PASSWORD_SIZE];
3024                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3025                         password[i] = data[2+i];
3026                 password[PASSWORD_SIZE-1] = 0;
3027                 if(strcmp(player->getPassword(),password))
3028                 {
3029                         // Wrong old password supplied!!
3030                         SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3031                         return;
3032                 }
3033                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3034                         password[i] = data[30+i];
3035                 player->updatePassword(password);
3036                 SendChatMessage(peer_id, L"Password change successful");
3037         }
3038         else
3039         {
3040                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3041                                 "unknown command "<<command<<std::endl;
3042         }
3043         
3044         } //try
3045         catch(SendFailedException &e)
3046         {
3047                 derr_server<<"Server::ProcessData(): SendFailedException: "
3048                                 <<"what="<<e.what()
3049                                 <<std::endl;
3050         }
3051 }
3052
3053 void Server::onMapEditEvent(MapEditEvent *event)
3054 {
3055         dstream<<"Server::onMapEditEvent()"<<std::endl;
3056         if(m_ignore_map_edit_events)
3057                 return;
3058         MapEditEvent *e = event->clone();
3059         m_unsent_map_edit_queue.push_back(e);
3060 }
3061
3062 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3063 {
3064         if(id == "current_player")
3065         {
3066                 assert(c->current_player);
3067                 return &(c->current_player->inventory);
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                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3080                 if(meta)
3081                         return meta->getInventory();
3082                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3083                                 <<"no metadata found"<<std::endl;
3084                 return NULL;
3085         }
3086
3087         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3088         return NULL;
3089 }
3090 void Server::inventoryModified(InventoryContext *c, std::string id)
3091 {
3092         if(id == "current_player")
3093         {
3094                 assert(c->current_player);
3095                 // Send inventory
3096                 UpdateCrafting(c->current_player->peer_id);
3097                 SendInventory(c->current_player->peer_id);
3098                 return;
3099         }
3100         
3101         Strfnd fn(id);
3102         std::string id0 = fn.next(":");
3103
3104         if(id0 == "nodemeta")
3105         {
3106                 v3s16 p;
3107                 p.X = stoi(fn.next(","));
3108                 p.Y = stoi(fn.next(","));
3109                 p.Z = stoi(fn.next(","));
3110                 v3s16 blockpos = getNodeBlockPos(p);
3111
3112                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3113                 if(meta)
3114                         meta->inventoryModified();
3115
3116                 for(core::map<u16, RemoteClient*>::Iterator
3117                         i = m_clients.getIterator();
3118                         i.atEnd()==false; i++)
3119                 {
3120                         RemoteClient *client = i.getNode()->getValue();
3121                         client->SetBlockNotSent(blockpos);
3122                 }
3123
3124                 return;
3125         }
3126
3127         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3128 }
3129
3130 core::list<PlayerInfo> Server::getPlayerInfo()
3131 {
3132         DSTACK(__FUNCTION_NAME);
3133         JMutexAutoLock envlock(m_env_mutex);
3134         JMutexAutoLock conlock(m_con_mutex);
3135         
3136         core::list<PlayerInfo> list;
3137
3138         core::list<Player*> players = m_env.getPlayers();
3139         
3140         core::list<Player*>::Iterator i;
3141         for(i = players.begin();
3142                         i != players.end(); i++)
3143         {
3144                 PlayerInfo info;
3145
3146                 Player *player = *i;
3147
3148                 try{
3149                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3150                         // Copy info from peer to info struct
3151                         info.id = peer->id;
3152                         info.address = peer->address;
3153                         info.avg_rtt = peer->avg_rtt;
3154                 }
3155                 catch(con::PeerNotFoundException &e)
3156                 {
3157                         // Set dummy peer info
3158                         info.id = 0;
3159                         info.address = Address(0,0,0,0,0);
3160                         info.avg_rtt = 0.0;
3161                 }
3162
3163                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3164                 info.position = player->getPosition();
3165
3166                 list.push_back(info);
3167         }
3168
3169         return list;
3170 }
3171
3172
3173 void Server::peerAdded(con::Peer *peer)
3174 {
3175         DSTACK(__FUNCTION_NAME);
3176         dout_server<<"Server::peerAdded(): peer->id="
3177                         <<peer->id<<std::endl;
3178         
3179         PeerChange c;
3180         c.type = PEER_ADDED;
3181         c.peer_id = peer->id;
3182         c.timeout = false;
3183         m_peer_change_queue.push_back(c);
3184 }
3185
3186 void Server::deletingPeer(con::Peer *peer, bool timeout)
3187 {
3188         DSTACK(__FUNCTION_NAME);
3189         dout_server<<"Server::deletingPeer(): peer->id="
3190                         <<peer->id<<", timeout="<<timeout<<std::endl;
3191         
3192         PeerChange c;
3193         c.type = PEER_REMOVED;
3194         c.peer_id = peer->id;
3195         c.timeout = timeout;
3196         m_peer_change_queue.push_back(c);
3197 }
3198
3199 /*
3200         Static send methods
3201 */
3202
3203 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3204 {
3205         DSTACK(__FUNCTION_NAME);
3206         std::ostringstream os(std::ios_base::binary);
3207
3208         writeU16(os, TOCLIENT_HP);
3209         writeU8(os, hp);
3210
3211         // Make data buffer
3212         std::string s = os.str();
3213         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3214         // Send as reliable
3215         con.Send(peer_id, 0, data, true);
3216 }
3217
3218 void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
3219 {
3220         DSTACK(__FUNCTION_NAME);
3221         std::ostringstream os(std::ios_base::binary);
3222
3223         writeU16(os, TOCLIENT_ACCESS_DENIED);
3224
3225         // Make data buffer
3226         std::string s = os.str();
3227         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3228         // Send as reliable
3229         con.Send(peer_id, 0, data, true);
3230 }
3231
3232 /*
3233         Non-static send methods
3234 */
3235
3236 void Server::SendObjectData(float dtime)
3237 {
3238         DSTACK(__FUNCTION_NAME);
3239
3240         core::map<v3s16, bool> stepped_blocks;
3241         
3242         for(core::map<u16, RemoteClient*>::Iterator
3243                 i = m_clients.getIterator();
3244                 i.atEnd() == false; i++)
3245         {
3246                 u16 peer_id = i.getNode()->getKey();
3247                 RemoteClient *client = i.getNode()->getValue();
3248                 assert(client->peer_id == peer_id);
3249                 
3250                 if(client->serialization_version == SER_FMT_VER_INVALID)
3251                         continue;
3252                 
3253                 client->SendObjectData(this, dtime, stepped_blocks);
3254         }
3255 }
3256
3257 void Server::SendPlayerInfos()
3258 {
3259         DSTACK(__FUNCTION_NAME);
3260
3261         //JMutexAutoLock envlock(m_env_mutex);
3262         
3263         // Get connected players
3264         core::list<Player*> players = m_env.getPlayers(true);
3265         
3266         u32 player_count = players.getSize();
3267         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3268
3269         SharedBuffer<u8> data(datasize);
3270         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3271         
3272         u32 start = 2;
3273         core::list<Player*>::Iterator i;
3274         for(i = players.begin();
3275                         i != players.end(); i++)
3276         {
3277                 Player *player = *i;
3278
3279                 /*dstream<<"Server sending player info for player with "
3280                                 "peer_id="<<player->peer_id<<std::endl;*/
3281                 
3282                 writeU16(&data[start], player->peer_id);
3283                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3284                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3285                 start += 2+PLAYERNAME_SIZE;
3286         }
3287
3288         //JMutexAutoLock conlock(m_con_mutex);
3289
3290         // Send as reliable
3291         m_con.SendToAll(0, data, true);
3292 }
3293
3294 void Server::SendInventory(u16 peer_id)
3295 {
3296         DSTACK(__FUNCTION_NAME);
3297         
3298         Player* player = m_env.getPlayer(peer_id);
3299         assert(player);
3300
3301         /*
3302                 Serialize it
3303         */
3304
3305         std::ostringstream os;
3306         //os.imbue(std::locale("C"));
3307
3308         player->inventory.serialize(os);
3309
3310         std::string s = os.str();
3311         
3312         SharedBuffer<u8> data(s.size()+2);
3313         writeU16(&data[0], TOCLIENT_INVENTORY);
3314         memcpy(&data[2], s.c_str(), s.size());
3315         
3316         // Send as reliable
3317         m_con.Send(peer_id, 0, data, true);
3318 }
3319
3320 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3321 {
3322         DSTACK(__FUNCTION_NAME);
3323         
3324         std::ostringstream os(std::ios_base::binary);
3325         u8 buf[12];
3326         
3327         // Write command
3328         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3329         os.write((char*)buf, 2);
3330         
3331         // Write length
3332         writeU16(buf, message.size());
3333         os.write((char*)buf, 2);
3334         
3335         // Write string
3336         for(u32 i=0; i<message.size(); i++)
3337         {
3338                 u16 w = message[i];
3339                 writeU16(buf, w);
3340                 os.write((char*)buf, 2);
3341         }
3342         
3343         // Make data buffer
3344         std::string s = os.str();
3345         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3346         // Send as reliable
3347         m_con.Send(peer_id, 0, data, true);
3348 }
3349
3350 void Server::BroadcastChatMessage(const std::wstring &message)
3351 {
3352         for(core::map<u16, RemoteClient*>::Iterator
3353                 i = m_clients.getIterator();
3354                 i.atEnd() == false; i++)
3355         {
3356                 // Get client and check that it is valid
3357                 RemoteClient *client = i.getNode()->getValue();
3358                 assert(client->peer_id == i.getNode()->getKey());
3359                 if(client->serialization_version == SER_FMT_VER_INVALID)
3360                         continue;
3361
3362                 SendChatMessage(client->peer_id, message);
3363         }
3364 }
3365
3366 void Server::SendPlayerHP(Player *player)
3367 {
3368         SendHP(m_con, player->peer_id, player->hp);
3369 }
3370
3371 void Server::SendMovePlayer(Player *player)
3372 {
3373         DSTACK(__FUNCTION_NAME);
3374         std::ostringstream os(std::ios_base::binary);
3375
3376         writeU16(os, TOCLIENT_MOVE_PLAYER);
3377         writeV3F1000(os, player->getPosition());
3378         writeF1000(os, player->getPitch());
3379         writeF1000(os, player->getYaw());
3380         
3381         {
3382                 v3f pos = player->getPosition();
3383                 f32 pitch = player->getPitch();
3384                 f32 yaw = player->getYaw();
3385                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3386                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3387                                 <<" pitch="<<pitch
3388                                 <<" yaw="<<yaw
3389                                 <<std::endl;
3390         }
3391
3392         // Make data buffer
3393         std::string s = os.str();
3394         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3395         // Send as reliable
3396         m_con.Send(player->peer_id, 0, data, true);
3397 }
3398
3399 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3400         core::list<u16> *far_players, float far_d_nodes)
3401 {
3402         float maxd = far_d_nodes*BS;
3403         v3f p_f = intToFloat(p, BS);
3404
3405         // Create packet
3406         u32 replysize = 8;
3407         SharedBuffer<u8> reply(replysize);
3408         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3409         writeS16(&reply[2], p.X);
3410         writeS16(&reply[4], p.Y);
3411         writeS16(&reply[6], p.Z);
3412
3413         for(core::map<u16, RemoteClient*>::Iterator
3414                 i = m_clients.getIterator();
3415                 i.atEnd() == false; i++)
3416         {
3417                 // Get client and check that it is valid
3418                 RemoteClient *client = i.getNode()->getValue();
3419                 assert(client->peer_id == i.getNode()->getKey());
3420                 if(client->serialization_version == SER_FMT_VER_INVALID)
3421                         continue;
3422
3423                 // Don't send if it's the same one
3424                 if(client->peer_id == ignore_id)
3425                         continue;
3426                 
3427                 if(far_players)
3428                 {
3429                         // Get player
3430                         Player *player = m_env.getPlayer(client->peer_id);
3431                         if(player)
3432                         {
3433                                 // If player is far away, only set modified blocks not sent
3434                                 v3f player_pos = player->getPosition();
3435                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3436                                 {
3437                                         far_players->push_back(client->peer_id);
3438                                         continue;
3439                                 }
3440                         }
3441                 }
3442
3443                 // Send as reliable
3444                 m_con.Send(client->peer_id, 0, reply, true);
3445         }
3446 }
3447
3448 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3449                 core::list<u16> *far_players, float far_d_nodes)
3450 {
3451         float maxd = far_d_nodes*BS;
3452         v3f p_f = intToFloat(p, BS);
3453
3454         for(core::map<u16, RemoteClient*>::Iterator
3455                 i = m_clients.getIterator();
3456                 i.atEnd() == false; i++)
3457         {
3458                 // Get client and check that it is valid
3459                 RemoteClient *client = i.getNode()->getValue();
3460                 assert(client->peer_id == i.getNode()->getKey());
3461                 if(client->serialization_version == SER_FMT_VER_INVALID)
3462                         continue;
3463
3464                 // Don't send if it's the same one
3465                 if(client->peer_id == ignore_id)
3466                         continue;
3467
3468                 if(far_players)
3469                 {
3470                         // Get player
3471                         Player *player = m_env.getPlayer(client->peer_id);
3472                         if(player)
3473                         {
3474                                 // If player is far away, only set modified blocks not sent
3475                                 v3f player_pos = player->getPosition();
3476                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3477                                 {
3478                                         far_players->push_back(client->peer_id);
3479                                         continue;
3480                                 }
3481                         }
3482                 }
3483
3484                 // Create packet
3485                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3486                 SharedBuffer<u8> reply(replysize);
3487                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3488                 writeS16(&reply[2], p.X);
3489                 writeS16(&reply[4], p.Y);
3490                 writeS16(&reply[6], p.Z);
3491                 n.serialize(&reply[8], client->serialization_version);
3492
3493                 // Send as reliable
3494                 m_con.Send(client->peer_id, 0, reply, true);
3495         }
3496 }
3497
3498 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3499 {
3500         DSTACK(__FUNCTION_NAME);
3501
3502         v3s16 p = block->getPos();
3503         
3504 #if 0
3505         // Analyze it a bit
3506         bool completely_air = true;
3507         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3508         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3509         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3510         {
3511                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3512                 {
3513                         completely_air = false;
3514                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3515                 }
3516         }
3517
3518         // Print result
3519         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3520         if(completely_air)
3521                 dstream<<"[completely air] ";
3522         dstream<<std::endl;
3523 #endif
3524
3525         /*
3526                 Create a packet with the block in the right format
3527         */
3528         
3529         std::ostringstream os(std::ios_base::binary);
3530         block->serialize(os, ver);
3531         std::string s = os.str();
3532         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3533
3534         u32 replysize = 8 + blockdata.getSize();
3535         SharedBuffer<u8> reply(replysize);
3536         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3537         writeS16(&reply[2], p.X);
3538         writeS16(&reply[4], p.Y);
3539         writeS16(&reply[6], p.Z);
3540         memcpy(&reply[8], *blockdata, blockdata.getSize());
3541
3542         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3543                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3544         
3545         /*
3546                 Send packet
3547         */
3548         m_con.Send(peer_id, 1, reply, true);
3549 }
3550
3551 void Server::SendBlocks(float dtime)
3552 {
3553         DSTACK(__FUNCTION_NAME);
3554
3555         JMutexAutoLock envlock(m_env_mutex);
3556         JMutexAutoLock conlock(m_con_mutex);
3557
3558         //TimeTaker timer("Server::SendBlocks");
3559
3560         core::array<PrioritySortedBlockTransfer> queue;
3561
3562         s32 total_sending = 0;
3563
3564         for(core::map<u16, RemoteClient*>::Iterator
3565                 i = m_clients.getIterator();
3566                 i.atEnd() == false; i++)
3567         {
3568                 RemoteClient *client = i.getNode()->getValue();
3569                 assert(client->peer_id == i.getNode()->getKey());
3570
3571                 total_sending += client->SendingCount();
3572                 
3573                 if(client->serialization_version == SER_FMT_VER_INVALID)
3574                         continue;
3575                 
3576                 client->GetNextBlocks(this, dtime, queue);
3577         }
3578
3579         // Sort.
3580         // Lowest priority number comes first.
3581         // Lowest is most important.
3582         queue.sort();
3583
3584         for(u32 i=0; i<queue.size(); i++)
3585         {
3586                 //TODO: Calculate limit dynamically
3587                 if(total_sending >= g_settings.getS32
3588                                 ("max_simultaneous_block_sends_server_total"))
3589                         break;
3590                 
3591                 PrioritySortedBlockTransfer q = queue[i];
3592
3593                 MapBlock *block = NULL;
3594                 try
3595                 {
3596                         block = m_env.getMap().getBlockNoCreate(q.pos);
3597                 }
3598                 catch(InvalidPositionException &e)
3599                 {
3600                         continue;
3601                 }
3602
3603                 RemoteClient *client = getClient(q.peer_id);
3604
3605                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3606
3607                 client->SentBlock(q.pos);
3608
3609                 total_sending++;
3610         }
3611 }
3612
3613 /*
3614         Something random
3615 */
3616
3617 void Server::UpdateCrafting(u16 peer_id)
3618 {
3619         DSTACK(__FUNCTION_NAME);
3620         
3621         Player* player = m_env.getPlayer(peer_id);
3622         assert(player);
3623
3624         /*
3625                 Calculate crafting stuff
3626         */
3627         if(g_settings.getBool("creative_mode") == false)
3628         {
3629                 InventoryList *clist = player->inventory.getList("craft");
3630                 InventoryList *rlist = player->inventory.getList("craftresult");
3631
3632                 if(rlist->getUsedSlots() == 0)
3633                         player->craftresult_is_preview = true;
3634
3635                 if(rlist && player->craftresult_is_preview)
3636                 {
3637                         rlist->clearItems();
3638                 }
3639                 if(clist && rlist && player->craftresult_is_preview)
3640                 {
3641                         InventoryItem *items[9];
3642                         for(u16 i=0; i<9; i++)
3643                         {
3644                                 items[i] = clist->getItem(i);
3645                         }
3646                         
3647                         bool found = false;
3648
3649                         // Wood
3650                         if(!found)
3651                         {
3652                                 ItemSpec specs[9];
3653                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3654                                 if(checkItemCombination(items, specs))
3655                                 {
3656                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3657                                         found = true;
3658                                 }
3659                         }
3660
3661                         // Stick
3662                         if(!found)
3663                         {
3664                                 ItemSpec specs[9];
3665                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3666                                 if(checkItemCombination(items, specs))
3667                                 {
3668                                         rlist->addItem(new CraftItem("Stick", 4));
3669                                         found = true;
3670                                 }
3671                         }
3672
3673                         // Fence
3674                         if(!found)
3675                         {
3676                                 ItemSpec specs[9];
3677                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3678                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3679                                 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3680                                 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3681                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3682                                 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3683                                 if(checkItemCombination(items, specs))
3684                                 {
3685                                         rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3686                                         found = true;
3687                                 }
3688                         }
3689
3690                         // Sign
3691                         if(!found)
3692                         {
3693                                 ItemSpec specs[9];
3694                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3695                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3696                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3697                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3698                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3699                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3700                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3701                                 if(checkItemCombination(items, specs))
3702                                 {
3703                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3704                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3705                                         found = true;
3706                                 }
3707                         }
3708
3709                         // Torch
3710                         if(!found)
3711                         {
3712                                 ItemSpec specs[9];
3713                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3714                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3715                                 if(checkItemCombination(items, specs))
3716                                 {
3717                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3718                                         found = true;
3719                                 }
3720                         }
3721
3722                         // Wooden pick
3723                         if(!found)
3724                         {
3725                                 ItemSpec specs[9];
3726                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3727                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3728                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
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("WPick", 0));
3734                                         found = true;
3735                                 }
3736                         }
3737
3738                         // Stone pick
3739                         if(!found)
3740                         {
3741                                 ItemSpec specs[9];
3742                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3743                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3744                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3745                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3746                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3747                                 if(checkItemCombination(items, specs))
3748                                 {
3749                                         rlist->addItem(new ToolItem("STPick", 0));
3750                                         found = true;
3751                                 }
3752                         }
3753
3754                         // Steel pick
3755                         if(!found)
3756                         {
3757                                 ItemSpec specs[9];
3758                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3759                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3760                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3761                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3762                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3763                                 if(checkItemCombination(items, specs))
3764                                 {
3765                                         rlist->addItem(new ToolItem("SteelPick", 0));
3766                                         found = true;
3767                                 }
3768                         }
3769
3770                         // Mese pick
3771                         if(!found)
3772                         {
3773                                 ItemSpec specs[9];
3774                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3775                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3776                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3777                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3778                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3779                                 if(checkItemCombination(items, specs))
3780                                 {
3781                                         rlist->addItem(new ToolItem("MesePick", 0));
3782                                         found = true;
3783                                 }
3784                         }
3785
3786                         // Wooden shovel
3787                         if(!found)
3788                         {
3789                                 ItemSpec specs[9];
3790                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3791                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3792                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3793                                 if(checkItemCombination(items, specs))
3794                                 {
3795                                         rlist->addItem(new ToolItem("WShovel", 0));
3796                                         found = true;
3797                                 }
3798                         }
3799
3800                         // Stone shovel
3801                         if(!found)
3802                         {
3803                                 ItemSpec specs[9];
3804                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3805                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3806                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3807                                 if(checkItemCombination(items, specs))
3808                                 {
3809                                         rlist->addItem(new ToolItem("STShovel", 0));
3810                                         found = true;
3811                                 }
3812                         }
3813
3814                         // Steel shovel
3815                         if(!found)
3816                         {
3817                                 ItemSpec specs[9];
3818                                 specs[1] = 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("SteelShovel", 0));
3824                                         found = true;
3825                                 }
3826                         }
3827
3828                         // Wooden axe
3829                         if(!found)
3830                         {
3831                                 ItemSpec specs[9];
3832                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3833                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3834                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3835                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3836                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3837                                 if(checkItemCombination(items, specs))
3838                                 {
3839                                         rlist->addItem(new ToolItem("WAxe", 0));
3840                                         found = true;
3841                                 }
3842                         }
3843
3844                         // Stone axe
3845                         if(!found)
3846                         {
3847                                 ItemSpec specs[9];
3848                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3849                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3850                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3851                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3852                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3853                                 if(checkItemCombination(items, specs))
3854                                 {
3855                                         rlist->addItem(new ToolItem("STAxe", 0));
3856                                         found = true;
3857                                 }
3858                         }
3859
3860                         // Steel axe
3861                         if(!found)
3862                         {
3863                                 ItemSpec specs[9];
3864                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3865                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3866                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3867                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3868                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3869                                 if(checkItemCombination(items, specs))
3870                                 {
3871                                         rlist->addItem(new ToolItem("SteelAxe", 0));
3872                                         found = true;
3873                                 }
3874                         }
3875
3876                         // Wooden sword
3877                         if(!found)
3878                         {
3879                                 ItemSpec specs[9];
3880                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3881                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3882                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3883                                 if(checkItemCombination(items, specs))
3884                                 {
3885                                         rlist->addItem(new ToolItem("WSword", 0));
3886                                         found = true;
3887                                 }
3888                         }
3889
3890                         // Stone sword
3891                         if(!found)
3892                         {
3893                                 ItemSpec specs[9];
3894                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3895                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3896                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3897                                 if(checkItemCombination(items, specs))
3898                                 {
3899                                         rlist->addItem(new ToolItem("STSword", 0));
3900                                         found = true;
3901                                 }
3902                         }
3903
3904                         // Steel sword
3905                         if(!found)
3906                         {
3907                                 ItemSpec specs[9];
3908                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3909                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3910                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3911                                 if(checkItemCombination(items, specs))
3912                                 {
3913                                         rlist->addItem(new ToolItem("SteelSword", 0));
3914                                         found = true;
3915                                 }
3916                         }
3917
3918                         // Chest
3919                         if(!found)
3920                         {
3921                                 ItemSpec specs[9];
3922                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3923                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3924                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3925                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3926                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3927                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3928                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3929                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3930                                 if(checkItemCombination(items, specs))
3931                                 {
3932                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
3933                                         found = true;
3934                                 }
3935                         }
3936
3937                         // Furnace
3938                         if(!found)
3939                         {
3940                                 ItemSpec specs[9];
3941                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3942                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3943                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3944                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3945                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3946                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3947                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3948                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3949                                 if(checkItemCombination(items, specs))
3950                                 {
3951                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
3952                                         found = true;
3953                                 }
3954                         }
3955
3956                         // Steel block
3957                         if(!found)
3958                         {
3959                                 ItemSpec specs[9];
3960                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3961                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3962                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3963                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3964                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3965                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3966                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3967                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3968                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3969                                 if(checkItemCombination(items, specs))
3970                                 {
3971                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
3972                                         found = true;
3973                                 }
3974                         }
3975
3976                         // Sandstone
3977                         if(!found)
3978                         {
3979                                 ItemSpec specs[9];
3980                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3981                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3982                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3983                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_SAND);
3984                                 if(checkItemCombination(items, specs))
3985                                 {
3986                                         rlist->addItem(new MaterialItem(CONTENT_SANDSTONE, 1));
3987                                         found = true;
3988                                 }
3989                         }
3990                 }
3991         
3992         } // if creative_mode == false
3993 }
3994
3995 RemoteClient* Server::getClient(u16 peer_id)
3996 {
3997         DSTACK(__FUNCTION_NAME);
3998         //JMutexAutoLock lock(m_con_mutex);
3999         core::map<u16, RemoteClient*>::Node *n;
4000         n = m_clients.find(peer_id);
4001         // A client should exist for all peers
4002         assert(n != NULL);
4003         return n->getValue();
4004 }
4005
4006 std::wstring Server::getStatusString()
4007 {
4008         std::wostringstream os(std::ios_base::binary);
4009         os<<L"# Server: ";
4010         // Version
4011         os<<L"version="<<narrow_to_wide(VERSION_STRING);
4012         // Uptime
4013         os<<L", uptime="<<m_uptime.get();
4014         // Information about clients
4015         os<<L", clients={";
4016         for(core::map<u16, RemoteClient*>::Iterator
4017                 i = m_clients.getIterator();
4018                 i.atEnd() == false; i++)
4019         {
4020                 // Get client and check that it is valid
4021                 RemoteClient *client = i.getNode()->getValue();
4022                 assert(client->peer_id == i.getNode()->getKey());
4023                 if(client->serialization_version == SER_FMT_VER_INVALID)
4024                         continue;
4025                 // Get player
4026                 Player *player = m_env.getPlayer(client->peer_id);
4027                 // Get name of player
4028                 std::wstring name = L"unknown";
4029                 if(player != NULL)
4030                         name = narrow_to_wide(player->getName());
4031                 // Add name to information string
4032                 os<<name<<L",";
4033         }
4034         os<<L"}";
4035         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4036                 os<<" WARNING: Map saving is disabled."<<std::endl;
4037         return os.str();
4038 }
4039
4040
4041 void setCreativeInventory(Player *player)
4042 {
4043         player->resetInventory();
4044         
4045         // Give some good tools
4046         {
4047                 InventoryItem *item = new ToolItem("MesePick", 0);
4048                 void* r = player->inventory.addItem("main", item);
4049                 assert(r == NULL);
4050         }
4051         {
4052                 InventoryItem *item = new ToolItem("SteelPick", 0);
4053                 void* r = player->inventory.addItem("main", item);
4054                 assert(r == NULL);
4055         }
4056         {
4057                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4058                 void* r = player->inventory.addItem("main", item);
4059                 assert(r == NULL);
4060         }
4061         {
4062                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4063                 void* r = player->inventory.addItem("main", item);
4064                 assert(r == NULL);
4065         }
4066
4067         /*
4068                 Give materials
4069         */
4070         
4071         // CONTENT_IGNORE-terminated list
4072         u8 material_items[] = {
4073                 CONTENT_TORCH,
4074                 CONTENT_COBBLE,
4075                 CONTENT_MUD,
4076                 CONTENT_STONE,
4077                 CONTENT_SAND,
4078                 CONTENT_SANDSTONE,
4079                 CONTENT_TREE,
4080                 CONTENT_LEAVES,
4081                 CONTENT_GLASS,
4082                 CONTENT_FENCE,
4083                 CONTENT_MESE,
4084                 CONTENT_WATERSOURCE,
4085                 CONTENT_CLOUD,
4086                 CONTENT_CHEST,
4087                 CONTENT_FURNACE,
4088                 CONTENT_SIGN_WALL,
4089                 CONTENT_IGNORE
4090         };
4091         
4092         u8 *mip = material_items;
4093         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4094         {
4095                 if(*mip == CONTENT_IGNORE)
4096                         break;
4097
4098                 InventoryItem *item = new MaterialItem(*mip, 1);
4099                 player->inventory.addItem("main", item);
4100
4101                 mip++;
4102         }
4103
4104 #if 0
4105         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4106         
4107         // add torch first
4108         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4109         player->inventory.addItem("main", item);
4110         
4111         // Then others
4112         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4113         {
4114                 // Skip some materials
4115                 if(i == CONTENT_WATER || i == CONTENT_TORCH
4116                         || i == CONTENT_COALSTONE)
4117                         continue;
4118
4119                 InventoryItem *item = new MaterialItem(i, 1);
4120                 player->inventory.addItem("main", item);
4121         }
4122 #endif
4123
4124         /*// Sign
4125         {
4126                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4127                 void* r = player->inventory.addItem("main", item);
4128                 assert(r == NULL);
4129         }*/
4130 }
4131
4132 v3f findSpawnPos(ServerMap &map)
4133 {
4134         //return v3f(50,50,50)*BS;
4135         
4136         v2s16 nodepos;
4137         s16 groundheight = 0;
4138         
4139         // Try to find a good place a few times
4140         for(s32 i=0; i<1000; i++)
4141         {
4142                 s32 range = 1 + i;
4143                 // We're going to try to throw the player to this position
4144                 nodepos = v2s16(-range + (myrand()%(range*2)),
4145                                 -range + (myrand()%(range*2)));
4146                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4147                 // Get sector (NOTE: Don't get because it's slow)
4148                 //m_env.getMap().emergeSector(sectorpos);
4149                 // Get ground height at point (fallbacks to heightmap function)
4150                 groundheight = map.findGroundLevel(nodepos);
4151                 // Don't go underwater
4152                 if(groundheight < WATER_LEVEL)
4153                 {
4154                         //dstream<<"-> Underwater"<<std::endl;
4155                         continue;
4156                 }
4157                 // Don't go to high places
4158                 if(groundheight > WATER_LEVEL + 4)
4159                 {
4160                         //dstream<<"-> Underwater"<<std::endl;
4161                         continue;
4162                 }
4163
4164                 // Found a good place
4165                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4166                 break;
4167         }
4168         
4169         // If no suitable place was not found, go above water at least.
4170         if(groundheight < WATER_LEVEL)
4171                 groundheight = WATER_LEVEL;
4172
4173         return intToFloat(v3s16(
4174                         nodepos.X,
4175                         groundheight + 2,
4176                         nodepos.Y
4177                         ), BS);
4178 }
4179
4180 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4181 {
4182         /*
4183                 Try to get an existing player
4184         */
4185         Player *player = m_env.getPlayer(name);
4186         if(player != NULL)
4187         {
4188                 // If player is already connected, cancel
4189                 if(player->peer_id != 0)
4190                 {
4191                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4192                         return NULL;
4193                 }
4194
4195                 // Got one.
4196                 player->peer_id = peer_id;
4197                 
4198                 // Reset inventory to creative if in creative mode
4199                 if(g_settings.getBool("creative_mode"))
4200                 {
4201                         setCreativeInventory(player);
4202                 }
4203
4204                 return player;
4205         }
4206
4207         /*
4208                 If player with the wanted peer_id already exists, cancel.
4209         */
4210         if(m_env.getPlayer(peer_id) != NULL)
4211         {
4212                 dstream<<"emergePlayer(): Player with wrong name but same"
4213                                 " peer_id already exists"<<std::endl;
4214                 return NULL;
4215         }
4216         
4217         /*
4218                 Create a new player
4219         */
4220         {
4221                 player = new ServerRemotePlayer();
4222                 //player->peer_id = c.peer_id;
4223                 //player->peer_id = PEER_ID_INEXISTENT;
4224                 player->peer_id = peer_id;
4225                 player->updateName(name);
4226                 player->updatePassword(password);
4227
4228                 if(g_settings.exists("default_privs"))
4229                                 player->privs = g_settings.getU64("default_privs");
4230
4231                 /*
4232                         Set player position
4233                 */
4234                 
4235                 dstream<<"Server: Finding spawn place for player \""
4236                                 <<player->getName()<<"\""<<std::endl;
4237
4238                 v3f pos = findSpawnPos(m_env.getServerMap());
4239
4240                 player->setPosition(pos);
4241
4242                 /*
4243                         Add player to environment
4244                 */
4245
4246                 m_env.addPlayer(player);
4247
4248                 /*
4249                         Add stuff to inventory
4250                 */
4251                 
4252                 if(g_settings.getBool("creative_mode"))
4253                 {
4254                         setCreativeInventory(player);
4255                 }
4256                 else if(g_settings.getBool("give_initial_stuff"))
4257                 {
4258                         {
4259                                 InventoryItem *item = new ToolItem("SteelPick", 0);
4260                                 void* r = player->inventory.addItem("main", item);
4261                                 assert(r == NULL);
4262                         }
4263                         {
4264                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4265                                 void* r = player->inventory.addItem("main", item);
4266                                 assert(r == NULL);
4267                         }
4268                         {
4269                                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4270                                 void* r = player->inventory.addItem("main", item);
4271                                 assert(r == NULL);
4272                         }
4273                         {
4274                                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4275                                 void* r = player->inventory.addItem("main", item);
4276                                 assert(r == NULL);
4277                         }
4278                         {
4279                                 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4280                                 void* r = player->inventory.addItem("main", item);
4281                                 assert(r == NULL);
4282                         }
4283                         /*{
4284                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4285                                 void* r = player->inventory.addItem("main", item);
4286                                 assert(r == NULL);
4287                         }
4288                         {
4289                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4290                                 void* r = player->inventory.addItem("main", item);
4291                                 assert(r == NULL);
4292                         }
4293                         {
4294                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4295                                 void* r = player->inventory.addItem("main", item);
4296                                 assert(r == NULL);
4297                         }
4298                         {
4299                                 InventoryItem *item = new CraftItem("Stick", 4);
4300                                 void* r = player->inventory.addItem("main", item);
4301                                 assert(r == NULL);
4302                         }
4303                         {
4304                                 InventoryItem *item = new ToolItem("WPick", 32000);
4305                                 void* r = player->inventory.addItem("main", item);
4306                                 assert(r == NULL);
4307                         }
4308                         {
4309                                 InventoryItem *item = new ToolItem("STPick", 32000);
4310                                 void* r = player->inventory.addItem("main", item);
4311                                 assert(r == NULL);
4312                         }*/
4313                         /*// and some signs
4314                         for(u16 i=0; i<4; i++)
4315                         {
4316                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4317                                 bool r = player->inventory.addItem("main", item);
4318                                 assert(r == true);
4319                         }*/
4320                         /*// Give some other stuff
4321                         {
4322                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4323                                 bool r = player->inventory.addItem("main", item);
4324                                 assert(r == true);
4325                         }*/
4326                 }
4327
4328                 return player;
4329                 
4330         } // create new player
4331 }
4332
4333 void Server::handlePeerChange(PeerChange &c)
4334 {
4335         JMutexAutoLock envlock(m_env_mutex);
4336         JMutexAutoLock conlock(m_con_mutex);
4337         
4338         if(c.type == PEER_ADDED)
4339         {
4340                 /*
4341                         Add
4342                 */
4343
4344                 // Error check
4345                 core::map<u16, RemoteClient*>::Node *n;
4346                 n = m_clients.find(c.peer_id);
4347                 // The client shouldn't already exist
4348                 assert(n == NULL);
4349
4350                 // Create client
4351                 RemoteClient *client = new RemoteClient();
4352                 client->peer_id = c.peer_id;
4353                 m_clients.insert(client->peer_id, client);
4354
4355         } // PEER_ADDED
4356         else if(c.type == PEER_REMOVED)
4357         {
4358                 /*
4359                         Delete
4360                 */
4361
4362                 // Error check
4363                 core::map<u16, RemoteClient*>::Node *n;
4364                 n = m_clients.find(c.peer_id);
4365                 // The client should exist
4366                 assert(n != NULL);
4367                 
4368                 /*
4369                         Mark objects to be not known by the client
4370                 */
4371                 RemoteClient *client = n->getValue();
4372                 // Handle objects
4373                 for(core::map<u16, bool>::Iterator
4374                                 i = client->m_known_objects.getIterator();
4375                                 i.atEnd()==false; i++)
4376                 {
4377                         // Get object
4378                         u16 id = i.getNode()->getKey();
4379                         ServerActiveObject* obj = m_env.getActiveObject(id);
4380                         
4381                         if(obj && obj->m_known_by_count > 0)
4382                                 obj->m_known_by_count--;
4383                 }
4384
4385                 // Collect information about leaving in chat
4386                 std::wstring message;
4387                 {
4388                         std::wstring name = L"unknown";
4389                         Player *player = m_env.getPlayer(c.peer_id);
4390                         if(player != NULL)
4391                                 name = narrow_to_wide(player->getName());
4392                         
4393                         message += L"*** ";
4394                         message += name;
4395                         message += L" left game";
4396                         if(c.timeout)
4397                                 message += L" (timed out)";
4398                 }
4399
4400                 /*// Delete player
4401                 {
4402                         m_env.removePlayer(c.peer_id);
4403                 }*/
4404
4405                 // Set player client disconnected
4406                 {
4407                         Player *player = m_env.getPlayer(c.peer_id);
4408                         if(player != NULL)
4409                                 player->peer_id = 0;
4410                 }
4411                 
4412                 // Delete client
4413                 delete m_clients[c.peer_id];
4414                 m_clients.remove(c.peer_id);
4415
4416                 // Send player info to all remaining clients
4417                 SendPlayerInfos();
4418                 
4419                 // Send leave chat message to all remaining clients
4420                 BroadcastChatMessage(message);
4421                 
4422         } // PEER_REMOVED
4423         else
4424         {
4425                 assert(0);
4426         }
4427 }
4428
4429 void Server::handlePeerChanges()
4430 {
4431         while(m_peer_change_queue.size() > 0)
4432         {
4433                 PeerChange c = m_peer_change_queue.pop_front();
4434
4435                 dout_server<<"Server: Handling peer change: "
4436                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4437                                 <<std::endl;
4438
4439                 handlePeerChange(c);
4440         }
4441 }
4442
4443 void dedicated_server_loop(Server &server, bool &kill)
4444 {
4445         DSTACK(__FUNCTION_NAME);
4446         
4447         std::cout<<DTIME<<std::endl;
4448         std::cout<<"========================"<<std::endl;
4449         std::cout<<"Running dedicated server"<<std::endl;
4450         std::cout<<"========================"<<std::endl;
4451         std::cout<<std::endl;
4452
4453         for(;;)
4454         {
4455                 // This is kind of a hack but can be done like this
4456                 // because server.step() is very light
4457                 sleep_ms(30);
4458                 server.step(0.030);
4459
4460                 if(server.getShutdownRequested() || kill)
4461                 {
4462                         std::cout<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4463                         break;
4464                 }
4465
4466                 static int counter = 0;
4467                 counter--;
4468                 if(counter <= 0)
4469                 {
4470                         counter = 10;
4471
4472                         core::list<PlayerInfo> list = server.getPlayerInfo();
4473                         core::list<PlayerInfo>::Iterator i;
4474                         static u32 sum_old = 0;
4475                         u32 sum = PIChecksum(list);
4476                         if(sum != sum_old)
4477                         {
4478                                 std::cout<<DTIME<<"Player info:"<<std::endl;
4479                                 for(i=list.begin(); i!=list.end(); i++)
4480                                 {
4481                                         i->PrintLine(&std::cout);
4482                                 }
4483                         }
4484                         sum_old = sum;
4485                 }
4486         }
4487 }
4488
4489