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