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