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