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