]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
9bcac5a6437bb09d21605dc487a45025b332a3c7
[minetest.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "server.h"
21 #include "utility.h"
22 #include <iostream>
23 #include "clientserver.h"
24 #include "map.h"
25 #include "jmutexautolock.h"
26 #include "main.h"
27 #include "constants.h"
28 #include "voxel.h"
29 #include "materials.h"
30 #include "mineral.h"
31 #include "config.h"
32 #include "servercommand.h"
33 #include "filesys.h"
34
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
36
37 void * ServerThread::Thread()
38 {
39         ThreadStarted();
40
41         DSTACK(__FUNCTION_NAME);
42
43         BEGIN_DEBUG_EXCEPTION_HANDLER
44
45         while(getRun())
46         {
47                 try{
48                         //TimeTaker timer("AsyncRunStep() + Receive()");
49
50                         {
51                                 //TimeTaker timer("AsyncRunStep()");
52                                 m_server->AsyncRunStep();
53                         }
54                 
55                         //dout_server<<"Running m_server->Receive()"<<std::endl;
56                         m_server->Receive();
57                 }
58                 catch(con::NoIncomingDataException &e)
59                 {
60                 }
61                 catch(con::PeerNotFoundException &e)
62                 {
63                         dout_server<<"Server: PeerNotFoundException"<<std::endl;
64                 }
65         }
66         
67         END_DEBUG_EXCEPTION_HANDLER
68
69         return NULL;
70 }
71
72 void * EmergeThread::Thread()
73 {
74         ThreadStarted();
75
76         DSTACK(__FUNCTION_NAME);
77
78         //bool debug=false;
79         
80         BEGIN_DEBUG_EXCEPTION_HANDLER
81
82         /*
83                 Get block info from queue, emerge them and send them
84                 to clients.
85
86                 After queue is empty, exit.
87         */
88         while(getRun())
89         {
90                 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
91                 if(qptr == NULL)
92                         break;
93                 
94                 SharedPtr<QueuedBlockEmerge> q(qptr);
95
96                 v3s16 &p = q->pos;
97                 v2s16 p2d(p.X,p.Z);
98
99                 /*
100                         Do not generate over-limit
101                 */
102                 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103                 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104                 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105                 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106                 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107                 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
108                         continue;
109                         
110                 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
111
112                 //TimeTaker timer("block emerge");
113                 
114                 /*
115                         Try to emerge it from somewhere.
116
117                         If it is only wanted as optional, only loading from disk
118                         will be allowed.
119                 */
120                 
121                 /*
122                         Check if any peer wants it as non-optional. In that case it
123                         will be generated.
124
125                         Also decrement the emerge queue count in clients.
126                 */
127
128                 bool optional = true;
129
130                 {
131                         core::map<u16, u8>::Iterator i;
132                         for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
133                         {
134                                 //u16 peer_id = i.getNode()->getKey();
135
136                                 // Check flags
137                                 u8 flags = i.getNode()->getValue();
138                                 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
139                                         optional = false;
140                                 
141                         }
142                 }
143
144                 /*dstream<<"EmergeThread: p="
145                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
146                                 <<"optional="<<optional<<std::endl;*/
147                 
148                 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
149                         
150                 core::map<v3s16, MapBlock*> changed_blocks;
151                 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
152
153                 MapBlock *block = NULL;
154                 bool got_block = true;
155                 core::map<v3s16, MapBlock*> modified_blocks;
156                 
157                 bool only_from_disk = false;
158                 
159                 if(optional)
160                         only_from_disk = true;
161
162                 v2s16 chunkpos = map.sector_to_chunk(p2d);
163
164                 bool generate_chunk = false;
165                 if(only_from_disk == false)
166                 {
167                         JMutexAutoLock envlock(m_server->m_env_mutex);
168                         if(map.chunkNonVolatile(chunkpos) == false)
169                                 generate_chunk = true;
170                 }
171                 if(generate_chunk)
172                 {
173                         ChunkMakeData data;
174                         
175                         {
176                                 JMutexAutoLock envlock(m_server->m_env_mutex);
177                                 map.initChunkMake(data, chunkpos);
178                         }
179
180                         makeChunk(&data);
181
182                         {
183                                 JMutexAutoLock envlock(m_server->m_env_mutex);
184                                 map.finishChunkMake(data, changed_blocks);
185                         }
186                 }
187         
188                 /*
189                         Fetch block from map or generate a single block
190                 */
191                 {
192                         JMutexAutoLock envlock(m_server->m_env_mutex);
193                         
194                         // Load sector if it isn't loaded
195                         if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196                                 map.loadSectorFull(p2d);
197
198                         block = map.getBlockNoCreateNoEx(p);
199                         if(!block || block->isDummy())
200                         {
201                                 if(only_from_disk)
202                                 {
203                                         got_block = false;
204                                 }
205                                 else
206                                 {
207                                         // Get, load or create sector
208                                         ServerMapSector *sector =
209                                                         (ServerMapSector*)map.createSector(p2d);
210                                         // Generate block
211                                         block = map.generateBlock(p, block, sector, changed_blocks,
212                                                         lighting_invalidated_blocks);
213                                         if(block == NULL)
214                                                 got_block = false;
215                                 }
216                         }
217                         else
218                         {
219                                 if(block->getLightingExpired()){
220                                         lighting_invalidated_blocks[block->getPos()] = block;
221                                 }
222                         }
223
224                         // TODO: Some additional checking and lighting updating,
225                         // see emergeBlock
226                 }
227
228                 {//envlock
229                 JMutexAutoLock envlock(m_server->m_env_mutex);
230                 
231                 if(got_block)
232                 {
233                         /*
234                                 Collect a list of blocks that have been modified in
235                                 addition to the fetched one.
236                         */
237                         
238                         if(lighting_invalidated_blocks.size() > 0)
239                         {
240                                 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
241                                                 <<" blocks"<<std::endl;*/
242                         
243                                 // 50-100ms for single block generation
244                                 //TimeTaker timer("** EmergeThread updateLighting");
245                                 
246                                 // Update lighting without locking the environment mutex,
247                                 // add modified blocks to changed blocks
248                                 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
249                         }
250                                 
251                         // Add all from changed_blocks to modified_blocks
252                         for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
253                                         i.atEnd() == false; i++)
254                         {
255                                 MapBlock *block = i.getNode()->getValue();
256                                 modified_blocks.insert(block->getPos(), block);
257                         }
258                 }
259                 // If we got no block, there should be no invalidated blocks
260                 else
261                 {
262                         assert(lighting_invalidated_blocks.size() == 0);
263                 }
264
265                 }//envlock
266
267                 /*
268                         Set sent status of modified blocks on clients
269                 */
270         
271                 // NOTE: Server's clients are also behind the connection mutex
272                 JMutexAutoLock lock(m_server->m_con_mutex);
273
274                 /*
275                         Add the originally fetched block to the modified list
276                 */
277                 if(got_block)
278                 {
279                         modified_blocks.insert(p, block);
280                 }
281                 
282                 /*
283                         Set the modified blocks unsent for all the clients
284                 */
285                 
286                 for(core::map<u16, RemoteClient*>::Iterator
287                                 i = m_server->m_clients.getIterator();
288                                 i.atEnd() == false; i++)
289                 {
290                         RemoteClient *client = i.getNode()->getValue();
291                         
292                         if(modified_blocks.size() > 0)
293                         {
294                                 // Remove block from sent history
295                                 client->SetBlocksNotSent(modified_blocks);
296                         }
297                 }
298                 
299         }
300
301         END_DEBUG_EXCEPTION_HANDLER
302
303         return NULL;
304 }
305
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307                 core::array<PrioritySortedBlockTransfer> &dest)
308 {
309         DSTACK(__FUNCTION_NAME);
310         
311         /*u32 timer_result;
312         TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
313         
314         // Increment timers
315         m_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         */
1678         {
1679                 //TimeTaker timer("Step node metadata");
1680
1681                 JMutexAutoLock envlock(m_env_mutex);
1682                 JMutexAutoLock conlock(m_con_mutex);
1683
1684                 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1685
1686                 core::map<v3s16, MapBlock*> changed_blocks;
1687                 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1688
1689                 for(core::map<v3s16, MapBlock*>::Iterator
1690                                 i = changed_blocks.getIterator();
1691                                 i.atEnd() == false; i++)
1692                 {
1693                         MapBlock *block = i.getNode()->getValue();
1694
1695                         for(core::map<u16, RemoteClient*>::Iterator
1696                                 i = m_clients.getIterator();
1697                                 i.atEnd()==false; i++)
1698                         {
1699                                 RemoteClient *client = i.getNode()->getValue();
1700                                 client->SetBlockNotSent(block->getPos());
1701                         }
1702                 }
1703         }
1704                 
1705         /*
1706                 Trigger emergethread (it somehow gets to a non-triggered but
1707                 bysy state sometimes)
1708         */
1709         {
1710                 float &counter = m_emergethread_trigger_timer;
1711                 counter += dtime;
1712                 if(counter >= 2.0)
1713                 {
1714                         counter = 0.0;
1715                         
1716                         m_emergethread.trigger();
1717                 }
1718         }
1719
1720         // Save map, players and auth stuff
1721         {
1722                 float &counter = m_savemap_timer;
1723                 counter += dtime;
1724                 if(counter >= g_settings.getFloat("server_map_save_interval"))
1725                 {
1726                         counter = 0.0;
1727
1728                         ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1729
1730                         // Auth stuff
1731                         m_authmanager.save();
1732                         
1733                         // Map
1734                         JMutexAutoLock lock(m_env_mutex);
1735                         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1736                         {
1737                                 // Save only changed parts
1738                                 m_env.getMap().save(true);
1739
1740                                 // Delete unused sectors
1741                                 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1742                                                 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1743                                 if(deleted_count > 0)
1744                                 {
1745                                         dout_server<<"Server: Unloaded "<<deleted_count
1746                                                         <<" sectors from memory"<<std::endl;
1747                                 }
1748
1749                                 // Save players
1750                                 m_env.serializePlayers(m_mapsavedir);
1751                                 
1752                                 // Save environment metadata
1753                                 m_env.saveMeta(m_mapsavedir);
1754                         }
1755                 }
1756         }
1757 }
1758
1759 void Server::Receive()
1760 {
1761         DSTACK(__FUNCTION_NAME);
1762         u32 data_maxsize = 10000;
1763         Buffer<u8> data(data_maxsize);
1764         u16 peer_id;
1765         u32 datasize;
1766         try{
1767                 {
1768                         JMutexAutoLock conlock(m_con_mutex);
1769                         datasize = m_con.Receive(peer_id, *data, data_maxsize);
1770                 }
1771
1772                 // This has to be called so that the client list gets synced
1773                 // with the peer list of the connection
1774                 handlePeerChanges();
1775
1776                 ProcessData(*data, datasize, peer_id);
1777         }
1778         catch(con::InvalidIncomingDataException &e)
1779         {
1780                 derr_server<<"Server::Receive(): "
1781                                 "InvalidIncomingDataException: what()="
1782                                 <<e.what()<<std::endl;
1783         }
1784         catch(con::PeerNotFoundException &e)
1785         {
1786                 //NOTE: This is not needed anymore
1787                 
1788                 // The peer has been disconnected.
1789                 // Find the associated player and remove it.
1790
1791                 /*JMutexAutoLock envlock(m_env_mutex);
1792
1793                 dout_server<<"ServerThread: peer_id="<<peer_id
1794                                 <<" has apparently closed connection. "
1795                                 <<"Removing player."<<std::endl;
1796
1797                 m_env.removePlayer(peer_id);*/
1798         }
1799 }
1800
1801 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1802 {
1803         DSTACK(__FUNCTION_NAME);
1804         // Environment is locked first.
1805         JMutexAutoLock envlock(m_env_mutex);
1806         JMutexAutoLock conlock(m_con_mutex);
1807         
1808         con::Peer *peer;
1809         try{
1810                 peer = m_con.GetPeer(peer_id);
1811         }
1812         catch(con::PeerNotFoundException &e)
1813         {
1814                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1815                                 <<peer_id<<" not found"<<std::endl;
1816                 return;
1817         }
1818         
1819         u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1820
1821         try
1822         {
1823
1824         if(datasize < 2)
1825                 return;
1826
1827         ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1828         
1829         if(command == TOSERVER_INIT)
1830         {
1831                 // [0] u16 TOSERVER_INIT
1832                 // [2] u8 SER_FMT_VER_HIGHEST
1833                 // [3] u8[20] player_name
1834                 // [23] u8[28] password <--- can be sent without this, from old versions
1835
1836                 if(datasize < 2+1+PLAYERNAME_SIZE)
1837                         return;
1838
1839                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1840                                 <<peer->id<<std::endl;
1841
1842                 // First byte after command is maximum supported
1843                 // serialization version
1844                 u8 client_max = data[2];
1845                 u8 our_max = SER_FMT_VER_HIGHEST;
1846                 // Use the highest version supported by both
1847                 u8 deployed = core::min_(client_max, our_max);
1848                 // If it's lower than the lowest supported, give up.
1849                 if(deployed < SER_FMT_VER_LOWEST)
1850                         deployed = SER_FMT_VER_INVALID;
1851
1852                 //peer->serialization_version = deployed;
1853                 getClient(peer->id)->pending_serialization_version = deployed;
1854
1855                 if(deployed == SER_FMT_VER_INVALID)
1856                 {
1857                         derr_server<<DTIME<<"Server: Cannot negotiate "
1858                                         "serialization version with peer "
1859                                         <<peer_id<<std::endl;
1860                         return;
1861                 }
1862
1863                 /*
1864                         Set up player
1865                 */
1866                 
1867                 // Get player name
1868                 char playername[PLAYERNAME_SIZE];
1869                 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1870                 {
1871                         playername[i] = data[3+i];
1872                 }
1873                 playername[PLAYERNAME_SIZE-1] = 0;
1874                 
1875                 if(playername[0]=='\0')
1876                 {
1877                         derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1878                         SendAccessDenied(m_con, peer_id,
1879                                         L"Empty name");
1880                         return;
1881                 }
1882
1883                 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1884                 {
1885                         derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1886                         SendAccessDenied(m_con, peer_id,
1887                                         L"Name contains unallowed characters");
1888                         return;
1889                 }
1890
1891                 // Get password
1892                 char password[PASSWORD_SIZE];
1893                 if(datasize == 2+1+PLAYERNAME_SIZE)
1894                 {
1895                         // old version - assume blank password
1896                         password[0] = 0;
1897                 }
1898                 else
1899                 {
1900                                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1901                                 {
1902                                         password[i] = data[23+i];
1903                                 }
1904                                 password[PASSWORD_SIZE-1] = 0;
1905                 }
1906                 
1907                 std::string checkpwd;
1908                 if(m_authmanager.exists(playername))
1909                 {
1910                         checkpwd = m_authmanager.getPassword(playername);
1911                 }
1912                 else
1913                 {
1914                         checkpwd = g_settings.get("default_password");
1915                 }
1916                 
1917                 if(password != checkpwd)
1918                 {
1919                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1920                                         <<": supplied invalid password for "
1921                                         <<playername<<std::endl;
1922                         SendAccessDenied(m_con, peer_id, L"Invalid password");
1923                         return;
1924                 }
1925                 
1926                 // Add player to auth manager
1927                 if(m_authmanager.exists(playername) == false)
1928                 {
1929                         derr_server<<DTIME<<"Server: adding player "<<playername
1930                                         <<" to auth manager"<<std::endl;
1931                         m_authmanager.add(playername);
1932                         m_authmanager.setPassword(playername, checkpwd);
1933                         m_authmanager.setPrivs(playername,
1934                                         stringToPrivs(g_settings.get("default_privs")));
1935                         m_authmanager.save();
1936                 }
1937
1938                 // Get player
1939                 Player *player = emergePlayer(playername, password, peer_id);
1940
1941
1942                 /*{
1943                         // DEBUG: Test serialization
1944                         std::ostringstream test_os;
1945                         player->serialize(test_os);
1946                         dstream<<"Player serialization test: \""<<test_os.str()
1947                                         <<"\""<<std::endl;
1948                         std::istringstream test_is(test_os.str());
1949                         player->deSerialize(test_is);
1950                 }*/
1951
1952                 // If failed, cancel
1953                 if(player == NULL)
1954                 {
1955                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1956                                         <<": failed to emerge player"<<std::endl;
1957                         return;
1958                 }
1959
1960                 /*
1961                 // If a client is already connected to the player, cancel
1962                 if(player->peer_id != 0)
1963                 {
1964                         derr_server<<DTIME<<"Server: peer_id="<<peer_id
1965                                         <<" tried to connect to "
1966                                         "an already connected player (peer_id="
1967                                         <<player->peer_id<<")"<<std::endl;
1968                         return;
1969                 }
1970                 // Set client of player
1971                 player->peer_id = peer_id;
1972                 */
1973
1974                 // Check if player doesn't exist
1975                 if(player == NULL)
1976                         throw con::InvalidIncomingDataException
1977                                 ("Server::ProcessData(): INIT: Player doesn't exist");
1978
1979                 /*// update name if it was supplied
1980                 if(datasize >= 20+3)
1981                 {
1982                         data[20+3-1] = 0;
1983                         player->updateName((const char*)&data[3]);
1984                 }*/
1985                 
1986                 /*
1987                         Answer with a TOCLIENT_INIT
1988                 */
1989                 {
1990                         SharedBuffer<u8> reply(2+1+6+8);
1991                         writeU16(&reply[0], TOCLIENT_INIT);
1992                         writeU8(&reply[2], deployed);
1993                         writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1994                         //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1995                         writeU64(&reply[2+1+6], 0); // no seed
1996                         
1997                         // Send as reliable
1998                         m_con.Send(peer_id, 0, reply, true);
1999                 }
2000
2001                 /*
2002                         Send complete position information
2003                 */
2004                 SendMovePlayer(player);
2005
2006                 return;
2007         }
2008
2009         if(command == TOSERVER_INIT2)
2010         {
2011                 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2012                                 <<peer->id<<std::endl;
2013
2014
2015                 getClient(peer->id)->serialization_version
2016                                 = getClient(peer->id)->pending_serialization_version;
2017
2018                 /*
2019                         Send some initialization data
2020                 */
2021                 
2022                 // Send player info to all players
2023                 SendPlayerInfos();
2024
2025                 // Send inventory to player
2026                 UpdateCrafting(peer->id);
2027                 SendInventory(peer->id);
2028
2029                 // Send HP
2030                 {
2031                         Player *player = m_env.getPlayer(peer_id);
2032                         SendPlayerHP(player);
2033                 }
2034                 
2035                 // Send time of day
2036                 {
2037                         SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2038                                         m_env.getTimeOfDay());
2039                         m_con.Send(peer->id, 0, data, true);
2040                 }
2041                 
2042                 // Send information about server to player in chat
2043                 SendChatMessage(peer_id, getStatusString());
2044                 
2045                 // Send information about joining in chat
2046                 {
2047                         std::wstring name = L"unknown";
2048                         Player *player = m_env.getPlayer(peer_id);
2049                         if(player != NULL)
2050                                 name = narrow_to_wide(player->getName());
2051                         
2052                         std::wstring message;
2053                         message += L"*** ";
2054                         message += name;
2055                         message += L" joined game";
2056                         BroadcastChatMessage(message);
2057                 }
2058
2059                 return;
2060         }
2061
2062         if(peer_ser_ver == SER_FMT_VER_INVALID)
2063         {
2064                 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2065                                 " serialization format invalid or not initialized."
2066                                 " Skipping incoming command="<<command<<std::endl;
2067                 return;
2068         }
2069         
2070         Player *player = m_env.getPlayer(peer_id);
2071
2072         if(player == NULL){
2073                 derr_server<<"Server::ProcessData(): Cancelling: "
2074                                 "No player for peer_id="<<peer_id
2075                                 <<std::endl;
2076                 return;
2077         }
2078         if(command == TOSERVER_PLAYERPOS)
2079         {
2080                 if(datasize < 2+12+12+4+4)
2081                         return;
2082         
2083                 u32 start = 0;
2084                 v3s32 ps = readV3S32(&data[start+2]);
2085                 v3s32 ss = readV3S32(&data[start+2+12]);
2086                 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2087                 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2088                 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2089                 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2090                 pitch = wrapDegrees(pitch);
2091                 yaw = wrapDegrees(yaw);
2092                 player->setPosition(position);
2093                 player->setSpeed(speed);
2094                 player->setPitch(pitch);
2095                 player->setYaw(yaw);
2096                 
2097                 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2098                                 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2099                                 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2100         }
2101         else if(command == TOSERVER_GOTBLOCKS)
2102         {
2103                 if(datasize < 2+1)
2104                         return;
2105                 
2106                 /*
2107                         [0] u16 command
2108                         [2] u8 count
2109                         [3] v3s16 pos_0
2110                         [3+6] v3s16 pos_1
2111                         ...
2112                 */
2113
2114                 u16 count = data[2];
2115                 for(u16 i=0; i<count; i++)
2116                 {
2117                         if((s16)datasize < 2+1+(i+1)*6)
2118                                 throw con::InvalidIncomingDataException
2119                                         ("GOTBLOCKS length is too short");
2120                         v3s16 p = readV3S16(&data[2+1+i*6]);
2121                         /*dstream<<"Server: GOTBLOCKS ("
2122                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2123                         RemoteClient *client = getClient(peer_id);
2124                         client->GotBlock(p);
2125                 }
2126         }
2127         else if(command == TOSERVER_DELETEDBLOCKS)
2128         {
2129                 if(datasize < 2+1)
2130                         return;
2131                 
2132                 /*
2133                         [0] u16 command
2134                         [2] u8 count
2135                         [3] v3s16 pos_0
2136                         [3+6] v3s16 pos_1
2137                         ...
2138                 */
2139
2140                 u16 count = data[2];
2141                 for(u16 i=0; i<count; i++)
2142                 {
2143                         if((s16)datasize < 2+1+(i+1)*6)
2144                                 throw con::InvalidIncomingDataException
2145                                         ("DELETEDBLOCKS length is too short");
2146                         v3s16 p = readV3S16(&data[2+1+i*6]);
2147                         /*dstream<<"Server: DELETEDBLOCKS ("
2148                                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2149                         RemoteClient *client = getClient(peer_id);
2150                         client->SetBlockNotSent(p);
2151                 }
2152         }
2153         else if(command == TOSERVER_CLICK_OBJECT)
2154         {
2155                 if(datasize < 13)
2156                         return;
2157
2158                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2159                         return;
2160
2161                 /*
2162                         [0] u16 command
2163                         [2] u8 button (0=left, 1=right)
2164                         [3] v3s16 block
2165                         [9] s16 id
2166                         [11] u16 item
2167                 */
2168                 u8 button = readU8(&data[2]);
2169                 v3s16 p;
2170                 p.X = readS16(&data[3]);
2171                 p.Y = readS16(&data[5]);
2172                 p.Z = readS16(&data[7]);
2173                 s16 id = readS16(&data[9]);
2174                 //u16 item_i = readU16(&data[11]);
2175
2176                 MapBlock *block = NULL;
2177                 try
2178                 {
2179                         block = m_env.getMap().getBlockNoCreate(p);
2180                 }
2181                 catch(InvalidPositionException &e)
2182                 {
2183                         derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2184                         return;
2185                 }
2186
2187                 MapBlockObject *obj = block->getObject(id);
2188
2189                 if(obj == NULL)
2190                 {
2191                         derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2192                         return;
2193                 }
2194
2195                 //TODO: Check that object is reasonably close
2196                 
2197                 // Left click
2198                 if(button == 0)
2199                 {
2200                         InventoryList *ilist = player->inventory.getList("main");
2201                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2202                         {
2203                         
2204                                 // Skip if inventory has no free space
2205                                 if(ilist->getUsedSlots() == ilist->getSize())
2206                                 {
2207                                         dout_server<<"Player inventory has no free space"<<std::endl;
2208                                         return;
2209                                 }
2210                                 
2211                                 /*
2212                                         Create the inventory item
2213                                 */
2214                                 InventoryItem *item = NULL;
2215                                 // If it is an item-object, take the item from it
2216                                 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2217                                 {
2218                                         item = ((ItemObject*)obj)->createInventoryItem();
2219                                 }
2220                                 // Else create an item of the object
2221                                 else
2222                                 {
2223                                         item = new MapBlockObjectItem
2224                                                         (obj->getInventoryString());
2225                                 }
2226                                 
2227                                 // Add to inventory and send inventory
2228                                 ilist->addItem(item);
2229                                 UpdateCrafting(player->peer_id);
2230                                 SendInventory(player->peer_id);
2231                         }
2232
2233                         // Remove from block
2234                         block->removeObject(id);
2235                 }
2236         }
2237         else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2238         {
2239                 if(datasize < 7)
2240                         return;
2241
2242                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2243                         return;
2244
2245                 /*
2246                         length: 7
2247                         [0] u16 command
2248                         [2] u8 button (0=left, 1=right)
2249                         [3] u16 id
2250                         [5] u16 item
2251                 */
2252                 u8 button = readU8(&data[2]);
2253                 u16 id = readS16(&data[3]);
2254                 u16 item_i = readU16(&data[11]);
2255         
2256                 ServerActiveObject *obj = m_env.getActiveObject(id);
2257
2258                 if(obj == NULL)
2259                 {
2260                         derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2261                                         <<std::endl;
2262                         return;
2263                 }
2264
2265                 //TODO: Check that object is reasonably close
2266                 
2267                 // Left click, pick object up (usually)
2268                 if(button == 0)
2269                 {
2270                         InventoryList *ilist = player->inventory.getList("main");
2271                         if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2272                         {
2273                         
2274                                 // Skip if inventory has no free space
2275                                 if(ilist->getUsedSlots() == ilist->getSize())
2276                                 {
2277                                         dout_server<<"Player inventory has no free space"<<std::endl;
2278                                         return;
2279                                 }
2280
2281                                 // Skip if object has been removed
2282                                 if(obj->m_removed)
2283                                         return;
2284                                 
2285                                 /*
2286                                         Create the inventory item
2287                                 */
2288                                 InventoryItem *item = obj->createPickedUpItem();
2289                                 
2290                                 if(item)
2291                                 {
2292                                         // Add to inventory and send inventory
2293                                         ilist->addItem(item);
2294                                         UpdateCrafting(player->peer_id);
2295                                         SendInventory(player->peer_id);
2296
2297                                         // Remove object from environment
2298                                         obj->m_removed = true;
2299                                 }
2300                                 else
2301                                 {
2302                                         /*
2303                                                 Item cannot be picked up. Punch it instead.
2304                                         */
2305
2306                                         ToolItem *titem = NULL;
2307                                         std::string toolname = "";
2308
2309                                         InventoryList *mlist = player->inventory.getList("main");
2310                                         if(mlist != NULL)
2311                                         {
2312                                                 InventoryItem *item = mlist->getItem(item_i);
2313                                                 if(item && (std::string)item->getName() == "ToolItem")
2314                                                 {
2315                                                         titem = (ToolItem*)item;
2316                                                         toolname = titem->getToolName();
2317                                                 }
2318                                         }
2319                                         
2320                                         u16 wear = obj->punch(toolname);
2321                                         
2322                                         if(titem)
2323                                         {
2324                                                 bool weared_out = titem->addWear(wear);
2325                                                 if(weared_out)
2326                                                         mlist->deleteItem(item_i);
2327                                                 SendInventory(player->peer_id);
2328                                         }
2329                                 }
2330                         }
2331                 }
2332         }
2333         else if(command == TOSERVER_GROUND_ACTION)
2334         {
2335                 if(datasize < 17)
2336                         return;
2337                 /*
2338                         length: 17
2339                         [0] u16 command
2340                         [2] u8 action
2341                         [3] v3s16 nodepos_undersurface
2342                         [9] v3s16 nodepos_abovesurface
2343                         [15] u16 item
2344                         actions:
2345                         0: start digging
2346                         1: place block
2347                         2: stop digging (all parameters ignored)
2348                         3: digging completed
2349                 */
2350                 u8 action = readU8(&data[2]);
2351                 v3s16 p_under;
2352                 p_under.X = readS16(&data[3]);
2353                 p_under.Y = readS16(&data[5]);
2354                 p_under.Z = readS16(&data[7]);
2355                 v3s16 p_over;
2356                 p_over.X = readS16(&data[9]);
2357                 p_over.Y = readS16(&data[11]);
2358                 p_over.Z = readS16(&data[13]);
2359                 u16 item_i = readU16(&data[15]);
2360
2361                 //TODO: Check that target is reasonably close
2362                 
2363                 /*
2364                         0: start digging
2365                 */
2366                 if(action == 0)
2367                 {
2368                         /*
2369                                 NOTE: This can be used in the future to check if
2370                                 somebody is cheating, by checking the timing.
2371                         */
2372                 } // action == 0
2373
2374                 /*
2375                         2: stop digging
2376                 */
2377                 else if(action == 2)
2378                 {
2379 #if 0
2380                         RemoteClient *client = getClient(peer->id);
2381                         JMutexAutoLock digmutex(client->m_dig_mutex);
2382                         client->m_dig_tool_item = -1;
2383 #endif
2384                 }
2385
2386                 /*
2387                         3: Digging completed
2388                 */
2389                 else if(action == 3)
2390                 {
2391                         // Mandatory parameter; actually used for nothing
2392                         core::map<v3s16, MapBlock*> modified_blocks;
2393
2394                         u8 material = CONTENT_IGNORE;
2395                         u8 mineral = MINERAL_NONE;
2396
2397                         bool cannot_remove_node = false;
2398
2399                         try
2400                         {
2401                                 MapNode n = m_env.getMap().getNode(p_under);
2402                                 // Get mineral
2403                                 mineral = n.getMineral();
2404                                 // Get material at position
2405                                 material = n.d;
2406                                 // If not yet cancelled
2407                                 if(cannot_remove_node == false)
2408                                 {
2409                                         // If it's not diggable, do nothing
2410                                         if(content_diggable(material) == false)
2411                                         {
2412                                                 derr_server<<"Server: Not finishing digging: "
2413                                                                 <<"Node not diggable"
2414                                                                 <<std::endl;
2415                                                 cannot_remove_node = true;
2416                                         }
2417                                 }
2418                                 // If not yet cancelled
2419                                 if(cannot_remove_node == false)
2420                                 {
2421                                         // Get node metadata
2422                                         NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2423                                         if(meta && meta->nodeRemovalDisabled() == true)
2424                                         {
2425                                                 derr_server<<"Server: Not finishing digging: "
2426                                                                 <<"Node metadata disables removal"
2427                                                                 <<std::endl;
2428                                                 cannot_remove_node = true;
2429                                         }
2430                                 }
2431                         }
2432                         catch(InvalidPositionException &e)
2433                         {
2434                                 derr_server<<"Server: Not finishing digging: Node not found."
2435                                                 <<" Adding block to emerge queue."
2436                                                 <<std::endl;
2437                                 m_emerge_queue.addBlock(peer_id,
2438                                                 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2439                                 cannot_remove_node = true;
2440                         }
2441
2442                         // Make sure the player is allowed to do it
2443                         if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2444                         {
2445                                 dstream<<"Player "<<player->getName()<<" cannot remove node"
2446                                                 <<" because privileges are "<<getPlayerPrivs(player)
2447                                                 <<std::endl;
2448                                 cannot_remove_node = true;
2449                         }
2450
2451                         /*
2452                                 If node can't be removed, set block to be re-sent to
2453                                 client and quit.
2454                         */
2455                         if(cannot_remove_node)
2456                         {
2457                                 derr_server<<"Server: Not finishing digging."<<std::endl;
2458
2459                                 // Client probably has wrong data.
2460                                 // Set block not sent, so that client will get
2461                                 // a valid one.
2462                                 dstream<<"Client "<<peer_id<<" tried to dig "
2463                                                 <<"node; but node cannot be removed."
2464                                                 <<" setting MapBlock not sent."<<std::endl;
2465                                 RemoteClient *client = getClient(peer_id);
2466                                 v3s16 blockpos = getNodeBlockPos(p_under);
2467                                 client->SetBlockNotSent(blockpos);
2468                                         
2469                                 return;
2470                         }
2471                         
2472                         /*
2473                                 Send the removal to all other clients.
2474                                 - If other player is close, send REMOVENODE
2475                                 - Otherwise set blocks not sent
2476                         */
2477                         core::list<u16> far_players;
2478                         sendRemoveNode(p_under, peer_id, &far_players, 100);
2479                         
2480                         /*
2481                                 Update and send inventory
2482                         */
2483
2484                         if(g_settings.getBool("creative_mode") == false)
2485                         {
2486                                 /*
2487                                         Wear out tool
2488                                 */
2489                                 InventoryList *mlist = player->inventory.getList("main");
2490                                 if(mlist != NULL)
2491                                 {
2492                                         InventoryItem *item = mlist->getItem(item_i);
2493                                         if(item && (std::string)item->getName() == "ToolItem")
2494                                         {
2495                                                 ToolItem *titem = (ToolItem*)item;
2496                                                 std::string toolname = titem->getToolName();
2497
2498                                                 // Get digging properties for material and tool
2499                                                 DiggingProperties prop =
2500                                                                 getDiggingProperties(material, toolname);
2501
2502                                                 if(prop.diggable == false)
2503                                                 {
2504                                                         derr_server<<"Server: WARNING: Player digged"
2505                                                                         <<" with impossible material + tool"
2506                                                                         <<" combination"<<std::endl;
2507                                                 }
2508                                                 
2509                                                 bool weared_out = titem->addWear(prop.wear);
2510
2511                                                 if(weared_out)
2512                                                 {
2513                                                         mlist->deleteItem(item_i);
2514                                                 }
2515                                         }
2516                                 }
2517
2518                                 /*
2519                                         Add dug item to inventory
2520                                 */
2521
2522                                 InventoryItem *item = NULL;
2523
2524                                 if(mineral != MINERAL_NONE)
2525                                         item = getDiggedMineralItem(mineral);
2526                                 
2527                                 // If not mineral
2528                                 if(item == NULL)
2529                                 {
2530                                         std::string &dug_s = content_features(material).dug_item;
2531                                         if(dug_s != "")
2532                                         {
2533                                                 std::istringstream is(dug_s, std::ios::binary);
2534                                                 item = InventoryItem::deSerialize(is);
2535                                         }
2536                                 }
2537                                 
2538                                 if(item != NULL)
2539                                 {
2540                                         // Add a item to inventory
2541                                         player->inventory.addItem("main", item);
2542
2543                                         // Send inventory
2544                                         UpdateCrafting(player->peer_id);
2545                                         SendInventory(player->peer_id);
2546                                 }
2547                         }
2548
2549                         /*
2550                                 Remove the node
2551                                 (this takes some time so it is done after the quick stuff)
2552                         */
2553                         m_ignore_map_edit_events = true;
2554                         m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2555                         m_ignore_map_edit_events = false;
2556                         
2557                         /*
2558                                 Set blocks not sent to far players
2559                         */
2560                         for(core::list<u16>::Iterator
2561                                         i = far_players.begin();
2562                                         i != far_players.end(); i++)
2563                         {
2564                                 u16 peer_id = *i;
2565                                 RemoteClient *client = getClient(peer_id);
2566                                 if(client==NULL)
2567                                         continue;
2568                                 client->SetBlocksNotSent(modified_blocks);
2569                         }
2570                 }
2571                 
2572                 /*
2573                         1: place block
2574                 */
2575                 else if(action == 1)
2576                 {
2577
2578                         InventoryList *ilist = player->inventory.getList("main");
2579                         if(ilist == NULL)
2580                                 return;
2581
2582                         // Get item
2583                         InventoryItem *item = ilist->getItem(item_i);
2584                         
2585                         // If there is no item, it is not possible to add it anywhere
2586                         if(item == NULL)
2587                                 return;
2588                         
2589                         /*
2590                                 Handle material items
2591                         */
2592                         if(std::string("MaterialItem") == item->getName())
2593                         {
2594                                 try{
2595                                         // Don't add a node if this is not a free space
2596                                         MapNode n2 = m_env.getMap().getNode(p_over);
2597                                         bool no_enough_privs =
2598                                                         ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2599                                         if(no_enough_privs)
2600                                                 dstream<<"Player "<<player->getName()<<" cannot add node"
2601                                                         <<" because privileges are "<<getPlayerPrivs(player)
2602                                                         <<std::endl;
2603
2604                                         if(content_buildable_to(n2.d) == false
2605                                                 || no_enough_privs)
2606                                         {
2607                                                 // Client probably has wrong data.
2608                                                 // Set block not sent, so that client will get
2609                                                 // a valid one.
2610                                                 dstream<<"Client "<<peer_id<<" tried to place"
2611                                                                 <<" node in invalid position; setting"
2612                                                                 <<" MapBlock not sent."<<std::endl;
2613                                                 RemoteClient *client = getClient(peer_id);
2614                                                 v3s16 blockpos = getNodeBlockPos(p_over);
2615                                                 client->SetBlockNotSent(blockpos);
2616                                                 return;
2617                                         }
2618                                 }
2619                                 catch(InvalidPositionException &e)
2620                                 {
2621                                         derr_server<<"Server: Ignoring ADDNODE: Node not found"
2622                                                         <<" Adding block to emerge queue."
2623                                                         <<std::endl;
2624                                         m_emerge_queue.addBlock(peer_id,
2625                                                         getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2626                                         return;
2627                                 }
2628
2629                                 // Reset build time counter
2630                                 getClient(peer->id)->m_time_from_building = 0.0;
2631                                 
2632                                 // Create node data
2633                                 MaterialItem *mitem = (MaterialItem*)item;
2634                                 MapNode n;
2635                                 n.d = mitem->getMaterial();
2636                                 if(content_features(n.d).wall_mounted)
2637                                         n.dir = packDir(p_under - p_over);
2638                                 
2639                                 /*
2640                                         Send to all players
2641                                 */
2642                                 core::list<u16> far_players;
2643                                 sendAddNode(p_over, n, 0, &far_players, 100);
2644                                 
2645                                 /*
2646                                         Handle inventory
2647                                 */
2648                                 InventoryList *ilist = player->inventory.getList("main");
2649                                 if(g_settings.getBool("creative_mode") == false && ilist)
2650                                 {
2651                                         // Remove from inventory and send inventory
2652                                         if(mitem->getCount() == 1)
2653                                                 ilist->deleteItem(item_i);
2654                                         else
2655                                                 mitem->remove(1);
2656                                         // Send inventory
2657                                         UpdateCrafting(peer_id);
2658                                         SendInventory(peer_id);
2659                                 }
2660                                 
2661                                 /*
2662                                         Add node.
2663
2664                                         This takes some time so it is done after the quick stuff
2665                                 */
2666                                 core::map<v3s16, MapBlock*> modified_blocks;
2667                                 m_ignore_map_edit_events = true;
2668                                 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2669                                 m_ignore_map_edit_events = false;
2670                                 
2671                                 /*
2672                                         Set blocks not sent to far players
2673                                 */
2674                                 for(core::list<u16>::Iterator
2675                                                 i = far_players.begin();
2676                                                 i != far_players.end(); i++)
2677                                 {
2678                                         u16 peer_id = *i;
2679                                         RemoteClient *client = getClient(peer_id);
2680                                         if(client==NULL)
2681                                                 continue;
2682                                         client->SetBlocksNotSent(modified_blocks);
2683                                 }
2684
2685                                 /*
2686                                         Calculate special events
2687                                 */
2688                                 
2689                                 /*if(n.d == CONTENT_MESE)
2690                                 {
2691                                         u32 count = 0;
2692                                         for(s16 z=-1; z<=1; z++)
2693                                         for(s16 y=-1; y<=1; y++)
2694                                         for(s16 x=-1; x<=1; x++)
2695                                         {
2696                                                 
2697                                         }
2698                                 }*/
2699                         }
2700                         /*
2701                                 Place other item (not a block)
2702                         */
2703                         else
2704                         {
2705                                 v3s16 blockpos = getNodeBlockPos(p_over);
2706                                 
2707                                 /*
2708                                         Check that the block is loaded so that the item
2709                                         can properly be added to the static list too
2710                                 */
2711                                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2712                                 if(block==NULL)
2713                                 {
2714                                         derr_server<<"Error while placing object: "
2715                                                         "block not found"<<std::endl;
2716                                         return;
2717                                 }
2718
2719                                 dout_server<<"Placing a miscellaneous item on map"
2720                                                 <<std::endl;
2721                                 
2722                                 // Calculate a position for it
2723                                 v3f pos = intToFloat(p_over, BS);
2724                                 //pos.Y -= BS*0.45;
2725                                 pos.Y -= BS*0.25; // let it drop a bit
2726                                 // Randomize a bit
2727                                 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2728                                 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2729
2730                                 /*
2731                                         Create the object
2732                                 */
2733                                 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2734
2735                                 if(obj == NULL)
2736                                 {
2737                                         derr_server<<"WARNING: item resulted in NULL object, "
2738                                                         <<"not placing onto map"
2739                                                         <<std::endl;
2740                                 }
2741                                 else
2742                                 {
2743                                         // Add the object to the environment
2744                                         m_env.addActiveObject(obj);
2745                                         
2746                                         dout_server<<"Placed object"<<std::endl;
2747
2748                                         if(g_settings.getBool("creative_mode") == false)
2749                                         {
2750                                                 // Delete the right amount of items from the slot
2751                                                 u16 dropcount = item->getDropCount();
2752                                                 
2753                                                 // Delete item if all gone
2754                                                 if(item->getCount() <= dropcount)
2755                                                 {
2756                                                         if(item->getCount() < dropcount)
2757                                                                 dstream<<"WARNING: Server: dropped more items"
2758                                                                                 <<" than the slot contains"<<std::endl;
2759                                                         
2760                                                         InventoryList *ilist = player->inventory.getList("main");
2761                                                         if(ilist)
2762                                                                 // Remove from inventory and send inventory
2763                                                                 ilist->deleteItem(item_i);
2764                                                 }
2765                                                 // Else decrement it
2766                                                 else
2767                                                         item->remove(dropcount);
2768                                                 
2769                                                 // Send inventory
2770                                                 UpdateCrafting(peer_id);
2771                                                 SendInventory(peer_id);
2772                                         }
2773                                 }
2774                         }
2775
2776                 } // action == 1
2777
2778                 /*
2779                         Catch invalid actions
2780                 */
2781                 else
2782                 {
2783                         derr_server<<"WARNING: Server: Invalid action "
2784                                         <<action<<std::endl;
2785                 }
2786         }
2787 #if 0
2788         else if(command == TOSERVER_RELEASE)
2789         {
2790                 if(datasize < 3)
2791                         return;
2792                 /*
2793                         length: 3
2794                         [0] u16 command
2795                         [2] u8 button
2796                 */
2797                 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2798         }
2799 #endif
2800         else if(command == TOSERVER_SIGNTEXT)
2801         {
2802                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2803                         return;
2804                 /*
2805                         u16 command
2806                         v3s16 blockpos
2807                         s16 id
2808                         u16 textlen
2809                         textdata
2810                 */
2811                 std::string datastring((char*)&data[2], datasize-2);
2812                 std::istringstream is(datastring, std::ios_base::binary);
2813                 u8 buf[6];
2814                 // Read stuff
2815                 is.read((char*)buf, 6);
2816                 v3s16 blockpos = readV3S16(buf);
2817                 is.read((char*)buf, 2);
2818                 s16 id = readS16(buf);
2819                 is.read((char*)buf, 2);
2820                 u16 textlen = readU16(buf);
2821                 std::string text;
2822                 for(u16 i=0; i<textlen; i++)
2823                 {
2824                         is.read((char*)buf, 1);
2825                         text += (char)buf[0];
2826                 }
2827
2828                 MapBlock *block = NULL;
2829                 try
2830                 {
2831                         block = m_env.getMap().getBlockNoCreate(blockpos);
2832                 }
2833                 catch(InvalidPositionException &e)
2834                 {
2835                         derr_server<<"Error while setting sign text: "
2836                                         "block not found"<<std::endl;
2837                         return;
2838                 }
2839
2840                 MapBlockObject *obj = block->getObject(id);
2841                 if(obj == NULL)
2842                 {
2843                         derr_server<<"Error while setting sign text: "
2844                                         "object not found"<<std::endl;
2845                         return;
2846                 }
2847                 
2848                 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2849                 {
2850                         derr_server<<"Error while setting sign text: "
2851                                         "object is not a sign"<<std::endl;
2852                         return;
2853                 }
2854
2855                 ((SignObject*)obj)->setText(text);
2856
2857                 obj->getBlock()->setChangedFlag();
2858         }
2859         else if(command == TOSERVER_SIGNNODETEXT)
2860         {
2861                 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2862                         return;
2863                 /*
2864                         u16 command
2865                         v3s16 p
2866                         u16 textlen
2867                         textdata
2868                 */
2869                 std::string datastring((char*)&data[2], datasize-2);
2870                 std::istringstream is(datastring, std::ios_base::binary);
2871                 u8 buf[6];
2872                 // Read stuff
2873                 is.read((char*)buf, 6);
2874                 v3s16 p = readV3S16(buf);
2875                 is.read((char*)buf, 2);
2876                 u16 textlen = readU16(buf);
2877                 std::string text;
2878                 for(u16 i=0; i<textlen; i++)
2879                 {
2880                         is.read((char*)buf, 1);
2881                         text += (char)buf[0];
2882                 }
2883
2884                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2885                 if(!meta)
2886                         return;
2887                 if(meta->typeId() != CONTENT_SIGN_WALL)
2888                         return;
2889                 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2890                 signmeta->setText(text);
2891                 
2892                 v3s16 blockpos = getNodeBlockPos(p);
2893                 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2894                 if(block)
2895                 {
2896                         block->setChangedFlag();
2897                 }
2898
2899                 for(core::map<u16, RemoteClient*>::Iterator
2900                         i = m_clients.getIterator();
2901                         i.atEnd()==false; i++)
2902                 {
2903                         RemoteClient *client = i.getNode()->getValue();
2904                         client->SetBlockNotSent(blockpos);
2905                 }
2906         }
2907         else if(command == TOSERVER_INVENTORY_ACTION)
2908         {
2909                 /*// Ignore inventory changes if in creative mode
2910                 if(g_settings.getBool("creative_mode") == true)
2911                 {
2912                         dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2913                                         <<std::endl;
2914                         return;
2915                 }*/
2916                 // Strip command and create a stream
2917                 std::string datastring((char*)&data[2], datasize-2);
2918                 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2919                 std::istringstream is(datastring, std::ios_base::binary);
2920                 // Create an action
2921                 InventoryAction *a = InventoryAction::deSerialize(is);
2922                 if(a != NULL)
2923                 {
2924                         // Create context
2925                         InventoryContext c;
2926                         c.current_player = player;
2927
2928                         /*
2929                                 Handle craftresult specially if not in creative mode
2930                         */
2931                         bool disable_action = false;
2932                         if(a->getType() == IACTION_MOVE
2933                                         && g_settings.getBool("creative_mode") == false)
2934                         {
2935                                 IMoveAction *ma = (IMoveAction*)a;
2936                                 if(ma->to_inv == "current_player" &&
2937                                                 ma->from_inv == "current_player")
2938                                 {
2939                                         InventoryList *rlist = player->inventory.getList("craftresult");
2940                                         assert(rlist);
2941                                         InventoryList *clist = player->inventory.getList("craft");
2942                                         assert(clist);
2943                                         InventoryList *mlist = player->inventory.getList("main");
2944                                         assert(mlist);
2945                                         /*
2946                                                 Craftresult is no longer preview if something
2947                                                 is moved into it
2948                                         */
2949                                         if(ma->to_list == "craftresult"
2950                                                         && ma->from_list != "craftresult")
2951                                         {
2952                                                 // If it currently is a preview, remove
2953                                                 // its contents
2954                                                 if(player->craftresult_is_preview)
2955                                                 {
2956                                                         rlist->deleteItem(0);
2957                                                 }
2958                                                 player->craftresult_is_preview = false;
2959                                         }
2960                                         /*
2961                                                 Crafting takes place if this condition is true.
2962                                         */
2963                                         if(player->craftresult_is_preview &&
2964                                                         ma->from_list == "craftresult")
2965                                         {
2966                                                 player->craftresult_is_preview = false;
2967                                                 clist->decrementMaterials(1);
2968                                         }
2969                                         /*
2970                                                 If the craftresult is placed on itself, move it to
2971                                                 main inventory instead of doing the action
2972                                         */
2973                                         if(ma->to_list == "craftresult"
2974                                                         && ma->from_list == "craftresult")
2975                                         {
2976                                                 disable_action = true;
2977                                                 
2978                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2979                                                 mlist->addItem(item1);
2980                                         }
2981                                 }
2982                         }
2983                         
2984                         if(disable_action == false)
2985                         {
2986                                 // Feed action to player inventory
2987                                 a->apply(&c, this);
2988                                 // Eat the action
2989                                 delete a;
2990                         }
2991                         else
2992                         {
2993                                 // Send inventory
2994                                 UpdateCrafting(player->peer_id);
2995                                 SendInventory(player->peer_id);
2996                         }
2997                 }
2998                 else
2999                 {
3000                         dstream<<"TOSERVER_INVENTORY_ACTION: "
3001                                         <<"InventoryAction::deSerialize() returned NULL"
3002                                         <<std::endl;
3003                 }
3004         }
3005         else if(command == TOSERVER_CHAT_MESSAGE)
3006         {
3007                 /*
3008                         u16 command
3009                         u16 length
3010                         wstring message
3011                 */
3012                 u8 buf[6];
3013                 std::string datastring((char*)&data[2], datasize-2);
3014                 std::istringstream is(datastring, std::ios_base::binary);
3015                 
3016                 // Read stuff
3017                 is.read((char*)buf, 2);
3018                 u16 len = readU16(buf);
3019                 
3020                 std::wstring message;
3021                 for(u16 i=0; i<len; i++)
3022                 {
3023                         is.read((char*)buf, 2);
3024                         message += (wchar_t)readU16(buf);
3025                 }
3026
3027                 // Get player name of this client
3028                 std::wstring name = narrow_to_wide(player->getName());
3029                 
3030                 // Line to send to players
3031                 std::wstring line;
3032                 // Whether to send to the player that sent the line
3033                 bool send_to_sender = false;
3034                 // Whether to send to other players
3035                 bool send_to_others = false;
3036                 
3037                 // Local player gets all privileges regardless of
3038                 // what's set on their account.
3039                 u64 privs = getPlayerPrivs(player);
3040
3041                 // Parse commands
3042                 std::wstring commandprefix = L"/#";
3043                 if(message.substr(0, commandprefix.size()) == commandprefix)
3044                 {
3045                         line += L"Server: ";
3046
3047                         message = message.substr(commandprefix.size());
3048
3049                         ServerCommandContext *ctx = new ServerCommandContext(
3050                                 str_split(message, L' '),
3051                                 this,
3052                                 &m_env,
3053                                 player,
3054                                 privs);
3055
3056                         line += processServerCommand(ctx);
3057                         send_to_sender = ctx->flags & 1;
3058                         send_to_others = ctx->flags & 2;
3059                         delete ctx;
3060
3061                 }
3062                 else
3063                 {
3064                         if(privs & PRIV_SHOUT)
3065                         {
3066                                 line += L"<";
3067                                 line += name;
3068                                 line += L"> ";
3069                                 line += message;
3070                                 send_to_others = true;
3071                         }
3072                         else
3073                         {
3074                                 line += L"Server: You are not allowed to shout";
3075                                 send_to_sender = true;
3076                         }
3077                 }
3078                 
3079                 if(line != L"")
3080                 {
3081                         dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3082
3083                         /*
3084                                 Send the message to clients
3085                         */
3086                         for(core::map<u16, RemoteClient*>::Iterator
3087                                 i = m_clients.getIterator();
3088                                 i.atEnd() == false; i++)
3089                         {
3090                                 // Get client and check that it is valid
3091                                 RemoteClient *client = i.getNode()->getValue();
3092                                 assert(client->peer_id == i.getNode()->getKey());
3093                                 if(client->serialization_version == SER_FMT_VER_INVALID)
3094                                         continue;
3095
3096                                 // Filter recipient
3097                                 bool sender_selected = (peer_id == client->peer_id);
3098                                 if(sender_selected == true && send_to_sender == false)
3099                                         continue;
3100                                 if(sender_selected == false && send_to_others == false)
3101                                         continue;
3102
3103                                 SendChatMessage(client->peer_id, line);
3104                         }
3105                 }
3106         }
3107         else if(command == TOSERVER_DAMAGE)
3108         {
3109                 if(g_settings.getBool("enable_damage"))
3110                 {
3111                         std::string datastring((char*)&data[2], datasize-2);
3112                         std::istringstream is(datastring, std::ios_base::binary);
3113                         u8 damage = readU8(is);
3114                         if(player->hp > damage)
3115                         {
3116                                 player->hp -= damage;
3117                         }
3118                         else
3119                         {
3120                                 player->hp = 0;
3121
3122                                 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3123                                                 <<std::endl;
3124                                 
3125                                 v3f pos = findSpawnPos(m_env.getServerMap());
3126                                 player->setPosition(pos);
3127                                 player->hp = 20;
3128                                 SendMovePlayer(player);
3129                                 SendPlayerHP(player);
3130                                 
3131                                 //TODO: Throw items around
3132                         }
3133                 }
3134
3135                 SendPlayerHP(player);
3136         }
3137         else if(command == TOSERVER_PASSWORD)
3138         {
3139                 /*
3140                         [0] u16 TOSERVER_PASSWORD
3141                         [2] u8[28] old password
3142                         [30] u8[28] new password
3143                 */
3144
3145                 if(datasize != 2+PASSWORD_SIZE*2)
3146                         return;
3147                 /*char password[PASSWORD_SIZE];
3148                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3149                         password[i] = data[2+i];
3150                 password[PASSWORD_SIZE-1] = 0;*/
3151                 std::string oldpwd;
3152                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3153                 {
3154                         char c = data[2+i];
3155                         if(c == 0)
3156                                 break;
3157                         oldpwd += c;
3158                 }
3159                 std::string newpwd;
3160                 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3161                 {
3162                         char c = data[2+PASSWORD_SIZE+i];
3163                         if(c == 0)
3164                                 break;
3165                         newpwd += c;
3166                 }
3167
3168                 std::string playername = player->getName();
3169
3170                 if(m_authmanager.exists(playername) == false)
3171                 {
3172                         dstream<<"Server: playername not found in authmanager"<<std::endl;
3173                         // Wrong old password supplied!!
3174                         SendChatMessage(peer_id, L"playername not found in authmanager");
3175                         return;
3176                 }
3177
3178                 std::string checkpwd = m_authmanager.getPassword(playername);
3179                 
3180                 if(oldpwd != checkpwd)
3181                 {
3182                         dstream<<"Server: invalid old password"<<std::endl;
3183                         // Wrong old password supplied!!
3184                         SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3185                         return;
3186                 }
3187
3188                 m_authmanager.setPassword(playername, newpwd);
3189                 
3190                 dstream<<"Server: password change successful for "<<playername
3191                                 <<std::endl;
3192                 SendChatMessage(peer_id, L"Password change successful");
3193         }
3194         else
3195         {
3196                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3197                                 "unknown command "<<command<<std::endl;
3198         }
3199         
3200         } //try
3201         catch(SendFailedException &e)
3202         {
3203                 derr_server<<"Server::ProcessData(): SendFailedException: "
3204                                 <<"what="<<e.what()
3205                                 <<std::endl;
3206         }
3207 }
3208
3209 void Server::onMapEditEvent(MapEditEvent *event)
3210 {
3211         dstream<<"Server::onMapEditEvent()"<<std::endl;
3212         if(m_ignore_map_edit_events)
3213                 return;
3214         MapEditEvent *e = event->clone();
3215         m_unsent_map_edit_queue.push_back(e);
3216 }
3217
3218 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3219 {
3220         if(id == "current_player")
3221         {
3222                 assert(c->current_player);
3223                 return &(c->current_player->inventory);
3224         }
3225         
3226         Strfnd fn(id);
3227         std::string id0 = fn.next(":");
3228
3229         if(id0 == "nodemeta")
3230         {
3231                 v3s16 p;
3232                 p.X = stoi(fn.next(","));
3233                 p.Y = stoi(fn.next(","));
3234                 p.Z = stoi(fn.next(","));
3235                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3236                 if(meta)
3237                         return meta->getInventory();
3238                 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3239                                 <<"no metadata found"<<std::endl;
3240                 return NULL;
3241         }
3242
3243         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3244         return NULL;
3245 }
3246 void Server::inventoryModified(InventoryContext *c, std::string id)
3247 {
3248         if(id == "current_player")
3249         {
3250                 assert(c->current_player);
3251                 // Send inventory
3252                 UpdateCrafting(c->current_player->peer_id);
3253                 SendInventory(c->current_player->peer_id);
3254                 return;
3255         }
3256         
3257         Strfnd fn(id);
3258         std::string id0 = fn.next(":");
3259
3260         if(id0 == "nodemeta")
3261         {
3262                 v3s16 p;
3263                 p.X = stoi(fn.next(","));
3264                 p.Y = stoi(fn.next(","));
3265                 p.Z = stoi(fn.next(","));
3266                 v3s16 blockpos = getNodeBlockPos(p);
3267
3268                 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3269                 if(meta)
3270                         meta->inventoryModified();
3271
3272                 for(core::map<u16, RemoteClient*>::Iterator
3273                         i = m_clients.getIterator();
3274                         i.atEnd()==false; i++)
3275                 {
3276                         RemoteClient *client = i.getNode()->getValue();
3277                         client->SetBlockNotSent(blockpos);
3278                 }
3279
3280                 return;
3281         }
3282
3283         dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3284 }
3285
3286 core::list<PlayerInfo> Server::getPlayerInfo()
3287 {
3288         DSTACK(__FUNCTION_NAME);
3289         JMutexAutoLock envlock(m_env_mutex);
3290         JMutexAutoLock conlock(m_con_mutex);
3291         
3292         core::list<PlayerInfo> list;
3293
3294         core::list<Player*> players = m_env.getPlayers();
3295         
3296         core::list<Player*>::Iterator i;
3297         for(i = players.begin();
3298                         i != players.end(); i++)
3299         {
3300                 PlayerInfo info;
3301
3302                 Player *player = *i;
3303
3304                 try{
3305                         con::Peer *peer = m_con.GetPeer(player->peer_id);
3306                         // Copy info from peer to info struct
3307                         info.id = peer->id;
3308                         info.address = peer->address;
3309                         info.avg_rtt = peer->avg_rtt;
3310                 }
3311                 catch(con::PeerNotFoundException &e)
3312                 {
3313                         // Set dummy peer info
3314                         info.id = 0;
3315                         info.address = Address(0,0,0,0,0);
3316                         info.avg_rtt = 0.0;
3317                 }
3318
3319                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3320                 info.position = player->getPosition();
3321
3322                 list.push_back(info);
3323         }
3324
3325         return list;
3326 }
3327
3328
3329 void Server::peerAdded(con::Peer *peer)
3330 {
3331         DSTACK(__FUNCTION_NAME);
3332         dout_server<<"Server::peerAdded(): peer->id="
3333                         <<peer->id<<std::endl;
3334         
3335         PeerChange c;
3336         c.type = PEER_ADDED;
3337         c.peer_id = peer->id;
3338         c.timeout = false;
3339         m_peer_change_queue.push_back(c);
3340 }
3341
3342 void Server::deletingPeer(con::Peer *peer, bool timeout)
3343 {
3344         DSTACK(__FUNCTION_NAME);
3345         dout_server<<"Server::deletingPeer(): peer->id="
3346                         <<peer->id<<", timeout="<<timeout<<std::endl;
3347         
3348         PeerChange c;
3349         c.type = PEER_REMOVED;
3350         c.peer_id = peer->id;
3351         c.timeout = timeout;
3352         m_peer_change_queue.push_back(c);
3353 }
3354
3355 /*
3356         Static send methods
3357 */
3358
3359 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3360 {
3361         DSTACK(__FUNCTION_NAME);
3362         std::ostringstream os(std::ios_base::binary);
3363
3364         writeU16(os, TOCLIENT_HP);
3365         writeU8(os, hp);
3366
3367         // Make data buffer
3368         std::string s = os.str();
3369         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3370         // Send as reliable
3371         con.Send(peer_id, 0, data, true);
3372 }
3373
3374 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3375                 const std::wstring &reason)
3376 {
3377         DSTACK(__FUNCTION_NAME);
3378         std::ostringstream os(std::ios_base::binary);
3379
3380         writeU16(os, TOCLIENT_ACCESS_DENIED);
3381         os<<serializeWideString(reason);
3382
3383         // Make data buffer
3384         std::string s = os.str();
3385         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3386         // Send as reliable
3387         con.Send(peer_id, 0, data, true);
3388 }
3389
3390 /*
3391         Non-static send methods
3392 */
3393
3394 void Server::SendObjectData(float dtime)
3395 {
3396         DSTACK(__FUNCTION_NAME);
3397
3398         core::map<v3s16, bool> stepped_blocks;
3399         
3400         for(core::map<u16, RemoteClient*>::Iterator
3401                 i = m_clients.getIterator();
3402                 i.atEnd() == false; i++)
3403         {
3404                 u16 peer_id = i.getNode()->getKey();
3405                 RemoteClient *client = i.getNode()->getValue();
3406                 assert(client->peer_id == peer_id);
3407                 
3408                 if(client->serialization_version == SER_FMT_VER_INVALID)
3409                         continue;
3410                 
3411                 client->SendObjectData(this, dtime, stepped_blocks);
3412         }
3413 }
3414
3415 void Server::SendPlayerInfos()
3416 {
3417         DSTACK(__FUNCTION_NAME);
3418
3419         //JMutexAutoLock envlock(m_env_mutex);
3420         
3421         // Get connected players
3422         core::list<Player*> players = m_env.getPlayers(true);
3423         
3424         u32 player_count = players.getSize();
3425         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3426
3427         SharedBuffer<u8> data(datasize);
3428         writeU16(&data[0], TOCLIENT_PLAYERINFO);
3429         
3430         u32 start = 2;
3431         core::list<Player*>::Iterator i;
3432         for(i = players.begin();
3433                         i != players.end(); i++)
3434         {
3435                 Player *player = *i;
3436
3437                 /*dstream<<"Server sending player info for player with "
3438                                 "peer_id="<<player->peer_id<<std::endl;*/
3439                 
3440                 writeU16(&data[start], player->peer_id);
3441                 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3442                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3443                 start += 2+PLAYERNAME_SIZE;
3444         }
3445
3446         //JMutexAutoLock conlock(m_con_mutex);
3447
3448         // Send as reliable
3449         m_con.SendToAll(0, data, true);
3450 }
3451
3452 void Server::SendInventory(u16 peer_id)
3453 {
3454         DSTACK(__FUNCTION_NAME);
3455         
3456         Player* player = m_env.getPlayer(peer_id);
3457         assert(player);
3458
3459         /*
3460                 Serialize it
3461         */
3462
3463         std::ostringstream os;
3464         //os.imbue(std::locale("C"));
3465
3466         player->inventory.serialize(os);
3467
3468         std::string s = os.str();
3469         
3470         SharedBuffer<u8> data(s.size()+2);
3471         writeU16(&data[0], TOCLIENT_INVENTORY);
3472         memcpy(&data[2], s.c_str(), s.size());
3473         
3474         // Send as reliable
3475         m_con.Send(peer_id, 0, data, true);
3476 }
3477
3478 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3479 {
3480         DSTACK(__FUNCTION_NAME);
3481         
3482         std::ostringstream os(std::ios_base::binary);
3483         u8 buf[12];
3484         
3485         // Write command
3486         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3487         os.write((char*)buf, 2);
3488         
3489         // Write length
3490         writeU16(buf, message.size());
3491         os.write((char*)buf, 2);
3492         
3493         // Write string
3494         for(u32 i=0; i<message.size(); i++)
3495         {
3496                 u16 w = message[i];
3497                 writeU16(buf, w);
3498                 os.write((char*)buf, 2);
3499         }
3500         
3501         // Make data buffer
3502         std::string s = os.str();
3503         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504         // Send as reliable
3505         m_con.Send(peer_id, 0, data, true);
3506 }
3507
3508 void Server::BroadcastChatMessage(const std::wstring &message)
3509 {
3510         for(core::map<u16, RemoteClient*>::Iterator
3511                 i = m_clients.getIterator();
3512                 i.atEnd() == false; i++)
3513         {
3514                 // Get client and check that it is valid
3515                 RemoteClient *client = i.getNode()->getValue();
3516                 assert(client->peer_id == i.getNode()->getKey());
3517                 if(client->serialization_version == SER_FMT_VER_INVALID)
3518                         continue;
3519
3520                 SendChatMessage(client->peer_id, message);
3521         }
3522 }
3523
3524 void Server::SendPlayerHP(Player *player)
3525 {
3526         SendHP(m_con, player->peer_id, player->hp);
3527 }
3528
3529 void Server::SendMovePlayer(Player *player)
3530 {
3531         DSTACK(__FUNCTION_NAME);
3532         std::ostringstream os(std::ios_base::binary);
3533
3534         writeU16(os, TOCLIENT_MOVE_PLAYER);
3535         writeV3F1000(os, player->getPosition());
3536         writeF1000(os, player->getPitch());
3537         writeF1000(os, player->getYaw());
3538         
3539         {
3540                 v3f pos = player->getPosition();
3541                 f32 pitch = player->getPitch();
3542                 f32 yaw = player->getYaw();
3543                 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3544                                 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3545                                 <<" pitch="<<pitch
3546                                 <<" yaw="<<yaw
3547                                 <<std::endl;
3548         }
3549
3550         // Make data buffer
3551         std::string s = os.str();
3552         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3553         // Send as reliable
3554         m_con.Send(player->peer_id, 0, data, true);
3555 }
3556
3557 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3558         core::list<u16> *far_players, float far_d_nodes)
3559 {
3560         float maxd = far_d_nodes*BS;
3561         v3f p_f = intToFloat(p, BS);
3562
3563         // Create packet
3564         u32 replysize = 8;
3565         SharedBuffer<u8> reply(replysize);
3566         writeU16(&reply[0], TOCLIENT_REMOVENODE);
3567         writeS16(&reply[2], p.X);
3568         writeS16(&reply[4], p.Y);
3569         writeS16(&reply[6], p.Z);
3570
3571         for(core::map<u16, RemoteClient*>::Iterator
3572                 i = m_clients.getIterator();
3573                 i.atEnd() == false; i++)
3574         {
3575                 // Get client and check that it is valid
3576                 RemoteClient *client = i.getNode()->getValue();
3577                 assert(client->peer_id == i.getNode()->getKey());
3578                 if(client->serialization_version == SER_FMT_VER_INVALID)
3579                         continue;
3580
3581                 // Don't send if it's the same one
3582                 if(client->peer_id == ignore_id)
3583                         continue;
3584                 
3585                 if(far_players)
3586                 {
3587                         // Get player
3588                         Player *player = m_env.getPlayer(client->peer_id);
3589                         if(player)
3590                         {
3591                                 // If player is far away, only set modified blocks not sent
3592                                 v3f player_pos = player->getPosition();
3593                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3594                                 {
3595                                         far_players->push_back(client->peer_id);
3596                                         continue;
3597                                 }
3598                         }
3599                 }
3600
3601                 // Send as reliable
3602                 m_con.Send(client->peer_id, 0, reply, true);
3603         }
3604 }
3605
3606 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3607                 core::list<u16> *far_players, float far_d_nodes)
3608 {
3609         float maxd = far_d_nodes*BS;
3610         v3f p_f = intToFloat(p, BS);
3611
3612         for(core::map<u16, RemoteClient*>::Iterator
3613                 i = m_clients.getIterator();
3614                 i.atEnd() == false; i++)
3615         {
3616                 // Get client and check that it is valid
3617                 RemoteClient *client = i.getNode()->getValue();
3618                 assert(client->peer_id == i.getNode()->getKey());
3619                 if(client->serialization_version == SER_FMT_VER_INVALID)
3620                         continue;
3621
3622                 // Don't send if it's the same one
3623                 if(client->peer_id == ignore_id)
3624                         continue;
3625
3626                 if(far_players)
3627                 {
3628                         // Get player
3629                         Player *player = m_env.getPlayer(client->peer_id);
3630                         if(player)
3631                         {
3632                                 // If player is far away, only set modified blocks not sent
3633                                 v3f player_pos = player->getPosition();
3634                                 if(player_pos.getDistanceFrom(p_f) > maxd)
3635                                 {
3636                                         far_players->push_back(client->peer_id);
3637                                         continue;
3638                                 }
3639                         }
3640                 }
3641
3642                 // Create packet
3643                 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3644                 SharedBuffer<u8> reply(replysize);
3645                 writeU16(&reply[0], TOCLIENT_ADDNODE);
3646                 writeS16(&reply[2], p.X);
3647                 writeS16(&reply[4], p.Y);
3648                 writeS16(&reply[6], p.Z);
3649                 n.serialize(&reply[8], client->serialization_version);
3650
3651                 // Send as reliable
3652                 m_con.Send(client->peer_id, 0, reply, true);
3653         }
3654 }
3655
3656 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3657 {
3658         DSTACK(__FUNCTION_NAME);
3659
3660         v3s16 p = block->getPos();
3661         
3662 #if 0
3663         // Analyze it a bit
3664         bool completely_air = true;
3665         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3666         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3667         for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3668         {
3669                 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3670                 {
3671                         completely_air = false;
3672                         x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3673                 }
3674         }
3675
3676         // Print result
3677         dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3678         if(completely_air)
3679                 dstream<<"[completely air] ";
3680         dstream<<std::endl;
3681 #endif
3682
3683         /*
3684                 Create a packet with the block in the right format
3685         */
3686         
3687         std::ostringstream os(std::ios_base::binary);
3688         block->serialize(os, ver);
3689         std::string s = os.str();
3690         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3691
3692         u32 replysize = 8 + blockdata.getSize();
3693         SharedBuffer<u8> reply(replysize);
3694         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3695         writeS16(&reply[2], p.X);
3696         writeS16(&reply[4], p.Y);
3697         writeS16(&reply[6], p.Z);
3698         memcpy(&reply[8], *blockdata, blockdata.getSize());
3699
3700         /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3701                         <<":  \tpacket size: "<<replysize<<std::endl;*/
3702         
3703         /*
3704                 Send packet
3705         */
3706         m_con.Send(peer_id, 1, reply, true);
3707 }
3708
3709 void Server::SendBlocks(float dtime)
3710 {
3711         DSTACK(__FUNCTION_NAME);
3712
3713         JMutexAutoLock envlock(m_env_mutex);
3714         JMutexAutoLock conlock(m_con_mutex);
3715
3716         //TimeTaker timer("Server::SendBlocks");
3717
3718         core::array<PrioritySortedBlockTransfer> queue;
3719
3720         s32 total_sending = 0;
3721         
3722         {
3723                 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3724
3725                 for(core::map<u16, RemoteClient*>::Iterator
3726                         i = m_clients.getIterator();
3727                         i.atEnd() == false; i++)
3728                 {
3729                         RemoteClient *client = i.getNode()->getValue();
3730                         assert(client->peer_id == i.getNode()->getKey());
3731
3732                         total_sending += client->SendingCount();
3733                         
3734                         if(client->serialization_version == SER_FMT_VER_INVALID)
3735                                 continue;
3736                         
3737                         client->GetNextBlocks(this, dtime, queue);
3738                 }
3739         }
3740
3741         // Sort.
3742         // Lowest priority number comes first.
3743         // Lowest is most important.
3744         queue.sort();
3745
3746         for(u32 i=0; i<queue.size(); i++)
3747         {
3748                 //TODO: Calculate limit dynamically
3749                 if(total_sending >= g_settings.getS32
3750                                 ("max_simultaneous_block_sends_server_total"))
3751                         break;
3752                 
3753                 PrioritySortedBlockTransfer q = queue[i];
3754
3755                 MapBlock *block = NULL;
3756                 try
3757                 {
3758                         block = m_env.getMap().getBlockNoCreate(q.pos);
3759                 }
3760                 catch(InvalidPositionException &e)
3761                 {
3762                         continue;
3763                 }
3764
3765                 RemoteClient *client = getClient(q.peer_id);
3766
3767                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3768
3769                 client->SentBlock(q.pos);
3770
3771                 total_sending++;
3772         }
3773 }
3774
3775 /*
3776         Something random
3777 */
3778
3779 void Server::UpdateCrafting(u16 peer_id)
3780 {
3781         DSTACK(__FUNCTION_NAME);
3782         
3783         Player* player = m_env.getPlayer(peer_id);
3784         assert(player);
3785
3786         /*
3787                 Calculate crafting stuff
3788         */
3789         if(g_settings.getBool("creative_mode") == false)
3790         {
3791                 InventoryList *clist = player->inventory.getList("craft");
3792                 InventoryList *rlist = player->inventory.getList("craftresult");
3793
3794                 if(rlist->getUsedSlots() == 0)
3795                         player->craftresult_is_preview = true;
3796
3797                 if(rlist && player->craftresult_is_preview)
3798                 {
3799                         rlist->clearItems();
3800                 }
3801                 if(clist && rlist && player->craftresult_is_preview)
3802                 {
3803                         InventoryItem *items[9];
3804                         for(u16 i=0; i<9; i++)
3805                         {
3806                                 items[i] = clist->getItem(i);
3807                         }
3808                         
3809                         bool found = false;
3810
3811                         // Wood
3812                         if(!found)
3813                         {
3814                                 ItemSpec specs[9];
3815                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3816                                 if(checkItemCombination(items, specs))
3817                                 {
3818                                         rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3819                                         found = true;
3820                                 }
3821                         }
3822
3823                         // Stick
3824                         if(!found)
3825                         {
3826                                 ItemSpec specs[9];
3827                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3828                                 if(checkItemCombination(items, specs))
3829                                 {
3830                                         rlist->addItem(new CraftItem("Stick", 4));
3831                                         found = true;
3832                                 }
3833                         }
3834
3835                         // Fence
3836                         if(!found)
3837                         {
3838                                 ItemSpec specs[9];
3839                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3840                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3841                                 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3842                                 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3843                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3844                                 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3845                                 if(checkItemCombination(items, specs))
3846                                 {
3847                                         rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3848                                         found = true;
3849                                 }
3850                         }
3851
3852                         // Sign
3853                         if(!found)
3854                         {
3855                                 ItemSpec specs[9];
3856                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3857                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3858                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3860                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3861                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3862                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3863                                 if(checkItemCombination(items, specs))
3864                                 {
3865                                         //rlist->addItem(new MapBlockObjectItem("Sign"));
3866                                         rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3867                                         found = true;
3868                                 }
3869                         }
3870
3871                         // Torch
3872                         if(!found)
3873                         {
3874                                 ItemSpec specs[9];
3875                                 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3876                                 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3877                                 if(checkItemCombination(items, specs))
3878                                 {
3879                                         rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3880                                         found = true;
3881                                 }
3882                         }
3883
3884                         // Wooden pick
3885                         if(!found)
3886                         {
3887                                 ItemSpec specs[9];
3888                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3889                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3890                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3891                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3892                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3893                                 if(checkItemCombination(items, specs))
3894                                 {
3895                                         rlist->addItem(new ToolItem("WPick", 0));
3896                                         found = true;
3897                                 }
3898                         }
3899
3900                         // Stone pick
3901                         if(!found)
3902                         {
3903                                 ItemSpec specs[9];
3904                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3905                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3906                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3907                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3908                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3909                                 if(checkItemCombination(items, specs))
3910                                 {
3911                                         rlist->addItem(new ToolItem("STPick", 0));
3912                                         found = true;
3913                                 }
3914                         }
3915
3916                         // Steel pick
3917                         if(!found)
3918                         {
3919                                 ItemSpec specs[9];
3920                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3921                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3922                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3923                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3924                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3925                                 if(checkItemCombination(items, specs))
3926                                 {
3927                                         rlist->addItem(new ToolItem("SteelPick", 0));
3928                                         found = true;
3929                                 }
3930                         }
3931
3932                         // Mese pick
3933                         if(!found)
3934                         {
3935                                 ItemSpec specs[9];
3936                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3937                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3938                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3939                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3940                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3941                                 if(checkItemCombination(items, specs))
3942                                 {
3943                                         rlist->addItem(new ToolItem("MesePick", 0));
3944                                         found = true;
3945                                 }
3946                         }
3947
3948                         // Wooden shovel
3949                         if(!found)
3950                         {
3951                                 ItemSpec specs[9];
3952                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3953                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3954                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3955                                 if(checkItemCombination(items, specs))
3956                                 {
3957                                         rlist->addItem(new ToolItem("WShovel", 0));
3958                                         found = true;
3959                                 }
3960                         }
3961
3962                         // Stone shovel
3963                         if(!found)
3964                         {
3965                                 ItemSpec specs[9];
3966                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3967                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3968                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3969                                 if(checkItemCombination(items, specs))
3970                                 {
3971                                         rlist->addItem(new ToolItem("STShovel", 0));
3972                                         found = true;
3973                                 }
3974                         }
3975
3976                         // Steel shovel
3977                         if(!found)
3978                         {
3979                                 ItemSpec specs[9];
3980                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3981                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3982                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3983                                 if(checkItemCombination(items, specs))
3984                                 {
3985                                         rlist->addItem(new ToolItem("SteelShovel", 0));
3986                                         found = true;
3987                                 }
3988                         }
3989
3990                         // Wooden axe
3991                         if(!found)
3992                         {
3993                                 ItemSpec specs[9];
3994                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3995                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3996                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3997                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3998                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3999                                 if(checkItemCombination(items, specs))
4000                                 {
4001                                         rlist->addItem(new ToolItem("WAxe", 0));
4002                                         found = true;
4003                                 }
4004                         }
4005
4006                         // Stone axe
4007                         if(!found)
4008                         {
4009                                 ItemSpec specs[9];
4010                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4011                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4012                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4013                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4014                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4015                                 if(checkItemCombination(items, specs))
4016                                 {
4017                                         rlist->addItem(new ToolItem("STAxe", 0));
4018                                         found = true;
4019                                 }
4020                         }
4021
4022                         // Steel axe
4023                         if(!found)
4024                         {
4025                                 ItemSpec specs[9];
4026                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4027                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4028                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4029                                 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4030                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4031                                 if(checkItemCombination(items, specs))
4032                                 {
4033                                         rlist->addItem(new ToolItem("SteelAxe", 0));
4034                                         found = true;
4035                                 }
4036                         }
4037
4038                         // Wooden sword
4039                         if(!found)
4040                         {
4041                                 ItemSpec specs[9];
4042                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4043                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4044                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4045                                 if(checkItemCombination(items, specs))
4046                                 {
4047                                         rlist->addItem(new ToolItem("WSword", 0));
4048                                         found = true;
4049                                 }
4050                         }
4051
4052                         // Stone sword
4053                         if(!found)
4054                         {
4055                                 ItemSpec specs[9];
4056                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4057                                 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4058                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4059                                 if(checkItemCombination(items, specs))
4060                                 {
4061                                         rlist->addItem(new ToolItem("STSword", 0));
4062                                         found = true;
4063                                 }
4064                         }
4065
4066                         // Steel sword
4067                         if(!found)
4068                         {
4069                                 ItemSpec specs[9];
4070                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4071                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4072                                 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4073                                 if(checkItemCombination(items, specs))
4074                                 {
4075                                         rlist->addItem(new ToolItem("SteelSword", 0));
4076                                         found = true;
4077                                 }
4078                         }
4079
4080                         // Chest
4081                         if(!found)
4082                         {
4083                                 ItemSpec specs[9];
4084                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4085                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4086                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4087                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4088                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4089                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4090                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4091                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4092                                 if(checkItemCombination(items, specs))
4093                                 {
4094                                         rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4095                                         found = true;
4096                                 }
4097                         }
4098
4099                         // Furnace
4100                         if(!found)
4101                         {
4102                                 ItemSpec specs[9];
4103                                 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4104                                 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4105                                 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4106                                 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4107                                 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4108                                 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4109                                 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4110                                 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4111                                 if(checkItemCombination(items, specs))
4112                                 {
4113                                         rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4114                                         found = true;
4115                                 }
4116                         }
4117
4118                         // Steel block
4119                         if(!found)
4120                         {
4121                                 ItemSpec specs[9];
4122                                 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4123                                 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4124                                 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4125                                 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4126                                 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4127                                 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4128                                 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4129                                 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4130                                 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4131                                 if(checkItemCombination(items, specs))
4132                                 {
4133                                         rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4134                                         found = true;
4135                                 }
4136                         }
4137                 }
4138         
4139         } // if creative_mode == false
4140 }
4141
4142 RemoteClient* Server::getClient(u16 peer_id)
4143 {
4144         DSTACK(__FUNCTION_NAME);
4145         //JMutexAutoLock lock(m_con_mutex);
4146         core::map<u16, RemoteClient*>::Node *n;
4147         n = m_clients.find(peer_id);
4148         // A client should exist for all peers
4149         assert(n != NULL);
4150         return n->getValue();
4151 }
4152
4153 std::wstring Server::getStatusString()
4154 {
4155         std::wostringstream os(std::ios_base::binary);
4156         os<<L"# Server: ";
4157         // Version
4158         os<<L"version="<<narrow_to_wide(VERSION_STRING);
4159         // Uptime
4160         os<<L", uptime="<<m_uptime.get();
4161         // Information about clients
4162         os<<L", clients={";
4163         for(core::map<u16, RemoteClient*>::Iterator
4164                 i = m_clients.getIterator();
4165                 i.atEnd() == false; i++)
4166         {
4167                 // Get client and check that it is valid
4168                 RemoteClient *client = i.getNode()->getValue();
4169                 assert(client->peer_id == i.getNode()->getKey());
4170                 if(client->serialization_version == SER_FMT_VER_INVALID)
4171                         continue;
4172                 // Get player
4173                 Player *player = m_env.getPlayer(client->peer_id);
4174                 // Get name of player
4175                 std::wstring name = L"unknown";
4176                 if(player != NULL)
4177                         name = narrow_to_wide(player->getName());
4178                 // Add name to information string
4179                 os<<name<<L",";
4180         }
4181         os<<L"}";
4182         if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4183                 os<<" WARNING: Map saving is disabled."<<std::endl;
4184         return os.str();
4185 }
4186
4187
4188 void setCreativeInventory(Player *player)
4189 {
4190         player->resetInventory();
4191         
4192         // Give some good tools
4193         {
4194                 InventoryItem *item = new ToolItem("MesePick", 0);
4195                 void* r = player->inventory.addItem("main", item);
4196                 assert(r == NULL);
4197         }
4198         {
4199                 InventoryItem *item = new ToolItem("SteelPick", 0);
4200                 void* r = player->inventory.addItem("main", item);
4201                 assert(r == NULL);
4202         }
4203         {
4204                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4205                 void* r = player->inventory.addItem("main", item);
4206                 assert(r == NULL);
4207         }
4208         {
4209                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4210                 void* r = player->inventory.addItem("main", item);
4211                 assert(r == NULL);
4212         }
4213
4214         /*
4215                 Give materials
4216         */
4217         
4218         // CONTENT_IGNORE-terminated list
4219         u8 material_items[] = {
4220                 CONTENT_TORCH,
4221                 CONTENT_COBBLE,
4222                 CONTENT_MUD,
4223                 CONTENT_STONE,
4224                 CONTENT_SAND,
4225                 CONTENT_TREE,
4226                 CONTENT_LEAVES,
4227                 CONTENT_GLASS,
4228                 CONTENT_FENCE,
4229                 CONTENT_MESE,
4230                 CONTENT_WATERSOURCE,
4231                 CONTENT_CLOUD,
4232                 CONTENT_CHEST,
4233                 CONTENT_FURNACE,
4234                 CONTENT_SIGN_WALL,
4235                 CONTENT_IGNORE
4236         };
4237         
4238         u8 *mip = material_items;
4239         for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4240         {
4241                 if(*mip == CONTENT_IGNORE)
4242                         break;
4243
4244                 InventoryItem *item = new MaterialItem(*mip, 1);
4245                 player->inventory.addItem("main", item);
4246
4247                 mip++;
4248         }
4249
4250 #if 0
4251         assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4252         
4253         // add torch first
4254         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4255         player->inventory.addItem("main", item);
4256         
4257         // Then others
4258         for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4259         {
4260                 // Skip some materials
4261                 if(i == CONTENT_WATER || i == CONTENT_TORCH
4262                         || i == CONTENT_COALSTONE)
4263                         continue;
4264
4265                 InventoryItem *item = new MaterialItem(i, 1);
4266                 player->inventory.addItem("main", item);
4267         }
4268 #endif
4269
4270         /*// Sign
4271         {
4272                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4273                 void* r = player->inventory.addItem("main", item);
4274                 assert(r == NULL);
4275         }*/
4276 }
4277
4278 v3f findSpawnPos(ServerMap &map)
4279 {
4280         //return v3f(50,50,50)*BS;
4281         
4282         v2s16 nodepos;
4283         s16 groundheight = 0;
4284         
4285         // Try to find a good place a few times
4286         for(s32 i=0; i<1000; i++)
4287         {
4288                 s32 range = 1 + i;
4289                 // We're going to try to throw the player to this position
4290                 nodepos = v2s16(-range + (myrand()%(range*2)),
4291                                 -range + (myrand()%(range*2)));
4292                 v2s16 sectorpos = getNodeSectorPos(nodepos);
4293                 // Get sector (NOTE: Don't get because it's slow)
4294                 //m_env.getMap().emergeSector(sectorpos);
4295                 // Get ground height at point (fallbacks to heightmap function)
4296                 groundheight = map.findGroundLevel(nodepos);
4297                 // Don't go underwater
4298                 if(groundheight < WATER_LEVEL)
4299                 {
4300                         //dstream<<"-> Underwater"<<std::endl;
4301                         continue;
4302                 }
4303                 // Don't go to high places
4304                 if(groundheight > WATER_LEVEL + 4)
4305                 {
4306                         //dstream<<"-> Underwater"<<std::endl;
4307                         continue;
4308                 }
4309
4310                 // Found a good place
4311                 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4312                 break;
4313         }
4314         
4315         // If no suitable place was not found, go above water at least.
4316         if(groundheight < WATER_LEVEL)
4317                 groundheight = WATER_LEVEL;
4318
4319         return intToFloat(v3s16(
4320                         nodepos.X,
4321                         groundheight + 2,
4322                         nodepos.Y
4323                         ), BS);
4324 }
4325
4326 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4327 {
4328         /*
4329                 Try to get an existing player
4330         */
4331         Player *player = m_env.getPlayer(name);
4332         if(player != NULL)
4333         {
4334                 // If player is already connected, cancel
4335                 if(player->peer_id != 0)
4336                 {
4337                         dstream<<"emergePlayer(): Player already connected"<<std::endl;
4338                         return NULL;
4339                 }
4340
4341                 // Got one.
4342                 player->peer_id = peer_id;
4343                 
4344                 // Reset inventory to creative if in creative mode
4345                 if(g_settings.getBool("creative_mode"))
4346                 {
4347                         setCreativeInventory(player);
4348                 }
4349
4350                 return player;
4351         }
4352
4353         /*
4354                 If player with the wanted peer_id already exists, cancel.
4355         */
4356         if(m_env.getPlayer(peer_id) != NULL)
4357         {
4358                 dstream<<"emergePlayer(): Player with wrong name but same"
4359                                 " peer_id already exists"<<std::endl;
4360                 return NULL;
4361         }
4362         
4363         /*
4364                 Create a new player
4365         */
4366         {
4367                 player = new ServerRemotePlayer();
4368                 //player->peer_id = c.peer_id;
4369                 //player->peer_id = PEER_ID_INEXISTENT;
4370                 player->peer_id = peer_id;
4371                 player->updateName(name);
4372                 m_authmanager.add(name);
4373                 m_authmanager.setPassword(name, password);
4374                 m_authmanager.setPrivs(name,
4375                                 stringToPrivs(g_settings.get("default_privs")));
4376
4377                 /*
4378                         Set player position
4379                 */
4380                 
4381                 dstream<<"Server: Finding spawn place for player \""
4382                                 <<player->getName()<<"\""<<std::endl;
4383
4384                 v3f pos = findSpawnPos(m_env.getServerMap());
4385
4386                 player->setPosition(pos);
4387
4388                 /*
4389                         Add player to environment
4390                 */
4391
4392                 m_env.addPlayer(player);
4393
4394                 /*
4395                         Add stuff to inventory
4396                 */
4397                 
4398                 if(g_settings.getBool("creative_mode"))
4399                 {
4400                         setCreativeInventory(player);
4401                 }
4402                 else if(g_settings.getBool("give_initial_stuff"))
4403                 {
4404                         {
4405                                 InventoryItem *item = new ToolItem("SteelPick", 0);
4406                                 void* r = player->inventory.addItem("main", item);
4407                                 assert(r == NULL);
4408                         }
4409                         {
4410                                 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4411                                 void* r = player->inventory.addItem("main", item);
4412                                 assert(r == NULL);
4413                         }
4414                         {
4415                                 InventoryItem *item = new ToolItem("SteelAxe", 0);
4416                                 void* r = player->inventory.addItem("main", item);
4417                                 assert(r == NULL);
4418                         }
4419                         {
4420                                 InventoryItem *item = new ToolItem("SteelShovel", 0);
4421                                 void* r = player->inventory.addItem("main", item);
4422                                 assert(r == NULL);
4423                         }
4424                         {
4425                                 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4426                                 void* r = player->inventory.addItem("main", item);
4427                                 assert(r == NULL);
4428                         }
4429                         /*{
4430                                 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4431                                 void* r = player->inventory.addItem("main", item);
4432                                 assert(r == NULL);
4433                         }
4434                         {
4435                                 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4436                                 void* r = player->inventory.addItem("main", item);
4437                                 assert(r == NULL);
4438                         }
4439                         {
4440                                 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4441                                 void* r = player->inventory.addItem("main", item);
4442                                 assert(r == NULL);
4443                         }
4444                         {
4445                                 InventoryItem *item = new CraftItem("Stick", 4);
4446                                 void* r = player->inventory.addItem("main", item);
4447                                 assert(r == NULL);
4448                         }
4449                         {
4450                                 InventoryItem *item = new ToolItem("WPick", 32000);
4451                                 void* r = player->inventory.addItem("main", item);
4452                                 assert(r == NULL);
4453                         }
4454                         {
4455                                 InventoryItem *item = new ToolItem("STPick", 32000);
4456                                 void* r = player->inventory.addItem("main", item);
4457                                 assert(r == NULL);
4458                         }*/
4459                         /*// and some signs
4460                         for(u16 i=0; i<4; i++)
4461                         {
4462                                 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4463                                 bool r = player->inventory.addItem("main", item);
4464                                 assert(r == true);
4465                         }*/
4466                         /*// Give some other stuff
4467                         {
4468                                 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4469                                 bool r = player->inventory.addItem("main", item);
4470                                 assert(r == true);
4471                         }*/
4472                 }
4473
4474                 return player;
4475                 
4476         } // create new player
4477 }
4478
4479 void Server::handlePeerChange(PeerChange &c)
4480 {
4481         JMutexAutoLock envlock(m_env_mutex);
4482         JMutexAutoLock conlock(m_con_mutex);
4483         
4484         if(c.type == PEER_ADDED)
4485         {
4486                 /*
4487                         Add
4488                 */
4489
4490                 // Error check
4491                 core::map<u16, RemoteClient*>::Node *n;
4492                 n = m_clients.find(c.peer_id);
4493                 // The client shouldn't already exist
4494                 assert(n == NULL);
4495
4496                 // Create client
4497                 RemoteClient *client = new RemoteClient();
4498                 client->peer_id = c.peer_id;
4499                 m_clients.insert(client->peer_id, client);
4500
4501         } // PEER_ADDED
4502         else if(c.type == PEER_REMOVED)
4503         {
4504                 /*
4505                         Delete
4506                 */
4507
4508                 // Error check
4509                 core::map<u16, RemoteClient*>::Node *n;
4510                 n = m_clients.find(c.peer_id);
4511                 // The client should exist
4512                 assert(n != NULL);
4513                 
4514                 /*
4515                         Mark objects to be not known by the client
4516                 */
4517                 RemoteClient *client = n->getValue();
4518                 // Handle objects
4519                 for(core::map<u16, bool>::Iterator
4520                                 i = client->m_known_objects.getIterator();
4521                                 i.atEnd()==false; i++)
4522                 {
4523                         // Get object
4524                         u16 id = i.getNode()->getKey();
4525                         ServerActiveObject* obj = m_env.getActiveObject(id);
4526                         
4527                         if(obj && obj->m_known_by_count > 0)
4528                                 obj->m_known_by_count--;
4529                 }
4530
4531                 // Collect information about leaving in chat
4532                 std::wstring message;
4533                 {
4534                         std::wstring name = L"unknown";
4535                         Player *player = m_env.getPlayer(c.peer_id);
4536                         if(player != NULL)
4537                                 name = narrow_to_wide(player->getName());
4538                         
4539                         message += L"*** ";
4540                         message += name;
4541                         message += L" left game";
4542                         if(c.timeout)
4543                                 message += L" (timed out)";
4544                 }
4545
4546                 /*// Delete player
4547                 {
4548                         m_env.removePlayer(c.peer_id);
4549                 }*/
4550
4551                 // Set player client disconnected
4552                 {
4553                         Player *player = m_env.getPlayer(c.peer_id);
4554                         if(player != NULL)
4555                                 player->peer_id = 0;
4556                 }
4557                 
4558                 // Delete client
4559                 delete m_clients[c.peer_id];
4560                 m_clients.remove(c.peer_id);
4561
4562                 // Send player info to all remaining clients
4563                 SendPlayerInfos();
4564                 
4565                 // Send leave chat message to all remaining clients
4566                 BroadcastChatMessage(message);
4567                 
4568         } // PEER_REMOVED
4569         else
4570         {
4571                 assert(0);
4572         }
4573 }
4574
4575 void Server::handlePeerChanges()
4576 {
4577         while(m_peer_change_queue.size() > 0)
4578         {
4579                 PeerChange c = m_peer_change_queue.pop_front();
4580
4581                 dout_server<<"Server: Handling peer change: "
4582                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4583                                 <<std::endl;
4584
4585                 handlePeerChange(c);
4586         }
4587 }
4588
4589 u64 Server::getPlayerPrivs(Player *player)
4590 {
4591         if(player==NULL)
4592                 return 0;
4593         std::string playername = player->getName();
4594         // Local player gets all privileges regardless of
4595         // what's set on their account.
4596         if(g_settings.get("name") == playername)
4597         {
4598                 return PRIV_ALL;
4599         }
4600         else
4601         {
4602                 return getPlayerAuthPrivs(playername);
4603         }
4604 }
4605
4606 void dedicated_server_loop(Server &server, bool &kill)
4607 {
4608         DSTACK(__FUNCTION_NAME);
4609         
4610         dstream<<DTIME<<std::endl;
4611         dstream<<"========================"<<std::endl;
4612         dstream<<"Running dedicated server"<<std::endl;
4613         dstream<<"========================"<<std::endl;
4614         dstream<<std::endl;
4615
4616         IntervalLimiter m_profiler_interval;
4617
4618         for(;;)
4619         {
4620                 // This is kind of a hack but can be done like this
4621                 // because server.step() is very light
4622                 {
4623                         ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4624                         sleep_ms(30);
4625                 }
4626                 server.step(0.030);
4627
4628                 if(server.getShutdownRequested() || kill)
4629                 {
4630                         dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4631                         break;
4632                 }
4633
4634                 /*
4635                         Profiler
4636                 */
4637                 float profiler_print_interval =
4638                                 g_settings.getFloat("profiler_print_interval");
4639                 if(profiler_print_interval != 0)
4640                 {
4641                         if(m_profiler_interval.step(0.030, profiler_print_interval))
4642                         {
4643                                 dstream<<"Profiler:"<<std::endl;
4644                                 g_profiler.print(dstream);
4645                                 g_profiler.clear();
4646                         }
4647                 }
4648                 
4649                 /*
4650                         Player info
4651                 */
4652                 static int counter = 0;
4653                 counter--;
4654                 if(counter <= 0)
4655                 {
4656                         counter = 10;
4657
4658                         core::list<PlayerInfo> list = server.getPlayerInfo();
4659                         core::list<PlayerInfo>::Iterator i;
4660                         static u32 sum_old = 0;
4661                         u32 sum = PIChecksum(list);
4662                         if(sum != sum_old)
4663                         {
4664                                 dstream<<DTIME<<"Player info:"<<std::endl;
4665                                 for(i=list.begin(); i!=list.end(); i++)
4666                                 {
4667                                         i->PrintLine(&dstream);
4668                                 }
4669                         }
4670                         sum_old = sum;
4671                 }
4672         }
4673 }
4674
4675