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