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