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