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