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