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