]> git.lizzy.rs Git - dragonfireclient.git/blob - src/server.cpp
some tinkering around
[dragonfireclient.git] / src / server.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "server.h"
25 #include "utility.h"
26 #include <iostream>
27 #include "clientserver.h"
28 #include "map.h"
29 #include "jmutexautolock.h"
30 #include "main.h"
31 #include "constants.h"
32 #include "voxel.h"
33 #include "materials.h"
34
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
2235                         */
2236                         bool disable_action = false;
2237                         if(a->getType() == IACTION_MOVE)
2238                         {
2239                                 IMoveAction *ma = (IMoveAction*)a;
2240                                 // Don't allow moving anything to craftresult
2241                                 if(ma->to_name == "craftresult")
2242                                 {
2243                                         // Do nothing
2244                                         disable_action = true;
2245                                 }
2246                                 // When something is removed from craftresult
2247                                 if(ma->from_name == "craftresult")
2248                                 {
2249                                         disable_action = true;
2250                                         // Remove stuff from craft
2251                                         InventoryList *clist = player->inventory.getList("craft");
2252                                         if(clist)
2253                                         {
2254                                                 u16 count = ma->count;
2255                                                 if(count == 0)
2256                                                         count = 1;
2257                                                 clist->decrementMaterials(count);
2258                                         }
2259                                         // Do action
2260                                         // Feed action to player inventory
2261                                         a->apply(&player->inventory);
2262                                         // Eat it
2263                                         delete a;
2264                                         // If something appeared in craftresult, throw it
2265                                         // in the main list
2266                                         InventoryList *rlist = player->inventory.getList("craftresult");
2267                                         InventoryList *mlist = player->inventory.getList("main");
2268                                         if(rlist && mlist && rlist->getUsedSlots() == 1)
2269                                         {
2270                                                 InventoryItem *item1 = rlist->changeItem(0, NULL);
2271                                                 mlist->addItem(item1);
2272                                         }
2273                                 }
2274                         }
2275                         if(disable_action == false)
2276                         {
2277                                 // Feed action to player inventory
2278                                 a->apply(&player->inventory);
2279                                 // Eat it
2280                                 delete a;
2281                         }
2282                         // Send inventory
2283                         SendInventory(player->peer_id);
2284                 }
2285                 else
2286                 {
2287                         dstream<<"TOSERVER_INVENTORY_ACTION: "
2288                                         <<"InventoryAction::deSerialize() returned NULL"
2289                                         <<std::endl;
2290                 }
2291         }
2292         else if(command == TOSERVER_CHAT_MESSAGE)
2293         {
2294                 /*
2295                         u16 command
2296                         u16 length
2297                         wstring message
2298                 */
2299                 u8 buf[6];
2300                 std::string datastring((char*)&data[2], datasize-2);
2301                 std::istringstream is(datastring, std::ios_base::binary);
2302                 
2303                 // Read stuff
2304                 is.read((char*)buf, 2);
2305                 u16 len = readU16(buf);
2306                 
2307                 std::wstring message;
2308                 for(u16 i=0; i<len; i++)
2309                 {
2310                         is.read((char*)buf, 2);
2311                         message += (wchar_t)readU16(buf);
2312                 }
2313
2314                 dstream<<"CHAT: "<<wide_to_narrow(message)<<std::endl;
2315
2316                 // Get player name of this client
2317                 std::wstring name = narrow_to_wide(player->getName());
2318
2319                 std::wstring line = std::wstring(L"<")+name+L"> "+message;
2320                 
2321                 /*
2322                         Send the message to all other clients
2323                 */
2324                 for(core::map<u16, RemoteClient*>::Iterator
2325                         i = m_clients.getIterator();
2326                         i.atEnd() == false; i++)
2327                 {
2328                         // Get client and check that it is valid
2329                         RemoteClient *client = i.getNode()->getValue();
2330                         assert(client->peer_id == i.getNode()->getKey());
2331                         if(client->serialization_version == SER_FMT_VER_INVALID)
2332                                 continue;
2333
2334                         // Don't send if it's the same one
2335                         if(peer_id == client->peer_id)
2336                                 continue;
2337
2338                         SendChatMessage(client->peer_id, line);
2339                 }
2340         }
2341         else
2342         {
2343                 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
2344                                 "unknown command "<<command<<std::endl;
2345         }
2346         
2347         } //try
2348         catch(SendFailedException &e)
2349         {
2350                 derr_server<<"Server::ProcessData(): SendFailedException: "
2351                                 <<"what="<<e.what()
2352                                 <<std::endl;
2353         }
2354 }
2355
2356 /*void Server::Send(u16 peer_id, u16 channelnum,
2357                 SharedBuffer<u8> data, bool reliable)
2358 {
2359         JMutexAutoLock lock(m_con_mutex);
2360         m_con.Send(peer_id, channelnum, data, reliable);
2361 }*/
2362
2363 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
2364 {
2365         DSTACK(__FUNCTION_NAME);
2366         /*
2367                 Create a packet with the block in the right format
2368         */
2369         
2370         std::ostringstream os(std::ios_base::binary);
2371         block->serialize(os, ver);
2372         std::string s = os.str();
2373         SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
2374
2375         u32 replysize = 8 + blockdata.getSize();
2376         SharedBuffer<u8> reply(replysize);
2377         v3s16 p = block->getPos();
2378         writeU16(&reply[0], TOCLIENT_BLOCKDATA);
2379         writeS16(&reply[2], p.X);
2380         writeS16(&reply[4], p.Y);
2381         writeS16(&reply[6], p.Z);
2382         memcpy(&reply[8], *blockdata, blockdata.getSize());
2383
2384         /*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2385                         <<":  \tpacket size: "<<replysize<<std::endl;*/
2386         
2387         /*
2388                 Send packet
2389         */
2390         m_con.Send(peer_id, 1, reply, true);
2391 }
2392
2393 core::list<PlayerInfo> Server::getPlayerInfo()
2394 {
2395         DSTACK(__FUNCTION_NAME);
2396         JMutexAutoLock envlock(m_env_mutex);
2397         JMutexAutoLock conlock(m_con_mutex);
2398         
2399         core::list<PlayerInfo> list;
2400
2401         core::list<Player*> players = m_env.getPlayers();
2402         
2403         core::list<Player*>::Iterator i;
2404         for(i = players.begin();
2405                         i != players.end(); i++)
2406         {
2407                 PlayerInfo info;
2408
2409                 Player *player = *i;
2410                 try{
2411                         con::Peer *peer = m_con.GetPeer(player->peer_id);
2412                         info.id = peer->id;
2413                         info.address = peer->address;
2414                         info.avg_rtt = peer->avg_rtt;
2415                 }
2416                 catch(con::PeerNotFoundException &e)
2417                 {
2418                         // Outdated peer info
2419                         info.id = 0;
2420                         info.address = Address(0,0,0,0,0);
2421                         info.avg_rtt = 0.0;
2422                 }
2423
2424                 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
2425                 info.position = player->getPosition();
2426
2427                 list.push_back(info);
2428         }
2429
2430         return list;
2431 }
2432
2433 void Server::peerAdded(con::Peer *peer)
2434 {
2435         DSTACK(__FUNCTION_NAME);
2436         dout_server<<"Server::peerAdded(): peer->id="
2437                         <<peer->id<<std::endl;
2438         
2439         PeerChange c;
2440         c.type = PEER_ADDED;
2441         c.peer_id = peer->id;
2442         c.timeout = false;
2443         m_peer_change_queue.push_back(c);
2444 }
2445
2446 void Server::deletingPeer(con::Peer *peer, bool timeout)
2447 {
2448         DSTACK(__FUNCTION_NAME);
2449         dout_server<<"Server::deletingPeer(): peer->id="
2450                         <<peer->id<<", timeout="<<timeout<<std::endl;
2451         
2452         PeerChange c;
2453         c.type = PEER_REMOVED;
2454         c.peer_id = peer->id;
2455         c.timeout = timeout;
2456         m_peer_change_queue.push_back(c);
2457 }
2458
2459 void Server::SendObjectData(float dtime)
2460 {
2461         DSTACK(__FUNCTION_NAME);
2462
2463         core::map<v3s16, bool> stepped_blocks;
2464         
2465         for(core::map<u16, RemoteClient*>::Iterator
2466                 i = m_clients.getIterator();
2467                 i.atEnd() == false; i++)
2468         {
2469                 u16 peer_id = i.getNode()->getKey();
2470                 RemoteClient *client = i.getNode()->getValue();
2471                 assert(client->peer_id == peer_id);
2472                 
2473                 if(client->serialization_version == SER_FMT_VER_INVALID)
2474                         continue;
2475                 
2476                 client->SendObjectData(this, dtime, stepped_blocks);
2477         }
2478 }
2479
2480 void Server::SendPlayerInfos()
2481 {
2482         DSTACK(__FUNCTION_NAME);
2483
2484         //JMutexAutoLock envlock(m_env_mutex);
2485         
2486         core::list<Player*> players = m_env.getPlayers();
2487         
2488         u32 player_count = players.getSize();
2489         u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
2490
2491         SharedBuffer<u8> data(datasize);
2492         writeU16(&data[0], TOCLIENT_PLAYERINFO);
2493         
2494         u32 start = 2;
2495         core::list<Player*>::Iterator i;
2496         for(i = players.begin();
2497                         i != players.end(); i++)
2498         {
2499                 Player *player = *i;
2500
2501                 /*dstream<<"Server sending player info for player with "
2502                                 "peer_id="<<player->peer_id<<std::endl;*/
2503                 
2504                 writeU16(&data[start], player->peer_id);
2505                 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
2506                 start += 2+PLAYERNAME_SIZE;
2507         }
2508
2509         //JMutexAutoLock conlock(m_con_mutex);
2510
2511         // Send as reliable
2512         m_con.SendToAll(0, data, true);
2513 }
2514
2515 enum ItemSpecType
2516 {
2517         ITEM_NONE,
2518         ITEM_MATERIAL,
2519         ITEM_CRAFT,
2520         ITEM_TOOL,
2521         ITEM_MBO
2522 };
2523
2524 struct ItemSpec
2525 {
2526         ItemSpec():
2527                 type(ITEM_NONE)
2528         {
2529         }
2530         ItemSpec(enum ItemSpecType a_type, std::string a_name):
2531                 type(a_type),
2532                 name(a_name),
2533                 num(65535)
2534         {
2535         }
2536         ItemSpec(enum ItemSpecType a_type, u16 a_num):
2537                 type(a_type),
2538                 name(""),
2539                 num(a_num)
2540         {
2541         }
2542         enum ItemSpecType type;
2543         // Only other one of these is used
2544         std::string name;
2545         u16 num;
2546 };
2547
2548 /*
2549         items: a pointer to an array of 9 pointers to items
2550         specs: a pointer to an array of 9 ItemSpecs
2551 */
2552 bool checkItemCombination(InventoryItem **items, ItemSpec *specs)
2553 {
2554         u16 items_min_x = 100;
2555         u16 items_max_x = 100;
2556         u16 items_min_y = 100;
2557         u16 items_max_y = 100;
2558         for(u16 y=0; y<3; y++)
2559         for(u16 x=0; x<3; x++)
2560         {
2561                 if(items[y*3 + x] == NULL)
2562                         continue;
2563                 if(items_min_x == 100 || x < items_min_x)
2564                         items_min_x = x;
2565                 if(items_min_y == 100 || y < items_min_y)
2566                         items_min_y = y;
2567                 if(items_max_x == 100 || x > items_max_x)
2568                         items_max_x = x;
2569                 if(items_max_y == 100 || y > items_max_y)
2570                         items_max_y = y;
2571         }
2572         // No items at all, just return false
2573         if(items_min_x == 100)
2574                 return false;
2575         
2576         u16 items_w = items_max_x - items_min_x + 1;
2577         u16 items_h = items_max_y - items_min_y + 1;
2578
2579         u16 specs_min_x = 100;
2580         u16 specs_max_x = 100;
2581         u16 specs_min_y = 100;
2582         u16 specs_max_y = 100;
2583         for(u16 y=0; y<3; y++)
2584         for(u16 x=0; x<3; x++)
2585         {
2586                 if(specs[y*3 + x].type == ITEM_NONE)
2587                         continue;
2588                 if(specs_min_x == 100 || x < specs_min_x)
2589                         specs_min_x = x;
2590                 if(specs_min_y == 100 || y < specs_min_y)
2591                         specs_min_y = y;
2592                 if(specs_max_x == 100 || x > specs_max_x)
2593                         specs_max_x = x;
2594                 if(specs_max_y == 100 || y > specs_max_y)
2595                         specs_max_y = y;
2596         }
2597         // No specs at all, just return false
2598         if(specs_min_x == 100)
2599                 return false;
2600
2601         u16 specs_w = specs_max_x - specs_min_x + 1;
2602         u16 specs_h = specs_max_y - specs_min_y + 1;
2603
2604         // Different sizes
2605         if(items_w != specs_w || items_h != specs_h)
2606                 return false;
2607
2608         for(u16 y=0; y<specs_h; y++)
2609         for(u16 x=0; x<specs_w; x++)
2610         {
2611                 u16 items_x = items_min_x + x;
2612                 u16 items_y = items_min_y + y;
2613                 u16 specs_x = specs_min_x + x;
2614                 u16 specs_y = specs_min_y + y;
2615                 InventoryItem *item = items[items_y * 3 + items_x];
2616                 ItemSpec &spec = specs[specs_y * 3 + specs_x];
2617                 
2618                 if(spec.type == ITEM_NONE)
2619                 {
2620                         // Has to be no item
2621                         if(item != NULL)
2622                                 return false;
2623                         continue;
2624                 }
2625                 
2626                 // There should be an item
2627                 if(item == NULL)
2628                         return false;
2629
2630                 std::string itemname = item->getName();
2631
2632                 if(spec.type == ITEM_MATERIAL)
2633                 {
2634                         if(itemname != "MaterialItem")
2635                                 return false;
2636                         MaterialItem *mitem = (MaterialItem*)item;
2637                         if(mitem->getMaterial() != spec.num)
2638                                 return false;
2639                 }
2640                 else if(spec.type == ITEM_CRAFT)
2641                 {
2642                         if(itemname != "CraftItem")
2643                                 return false;
2644                         CraftItem *mitem = (CraftItem*)item;
2645                         if(mitem->getSubName() != spec.name)
2646                                 return false;
2647                 }
2648                 else if(spec.type == ITEM_TOOL)
2649                 {
2650                         // Not supported yet
2651                         assert(0);
2652                 }
2653                 else if(spec.type == ITEM_MBO)
2654                 {
2655                         // Not supported yet
2656                         assert(0);
2657                 }
2658                 else
2659                 {
2660                         // Not supported yet
2661                         assert(0);
2662                 }
2663         }
2664
2665         return true;
2666 }
2667
2668 void Server::SendInventory(u16 peer_id)
2669 {
2670         DSTACK(__FUNCTION_NAME);
2671         
2672         Player* player = m_env.getPlayer(peer_id);
2673
2674         /*
2675                 Calculate crafting stuff
2676         */
2677
2678         InventoryList *clist = player->inventory.getList("craft");
2679         InventoryList *rlist = player->inventory.getList("craftresult");
2680         if(rlist)
2681         {
2682                 rlist->clearItems();
2683         }
2684         if(clist && rlist)
2685         {
2686                 InventoryItem *items[9];
2687                 for(u16 i=0; i<9; i++)
2688                 {
2689                         items[i] = clist->getItem(i);
2690                 }
2691                 
2692                 bool found = false;
2693
2694                 // Wood
2695                 if(!found)
2696                 {
2697                         ItemSpec specs[9];
2698                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
2699                         if(checkItemCombination(items, specs))
2700                         {
2701                                 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
2702                                 found = true;
2703                         }
2704                 }
2705
2706                 // Stick
2707                 if(!found)
2708                 {
2709                         ItemSpec specs[9];
2710                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2711                         if(checkItemCombination(items, specs))
2712                         {
2713                                 rlist->addItem(new CraftItem("Stick", 4));
2714                                 found = true;
2715                         }
2716                 }
2717
2718                 // Sign
2719                 if(!found)
2720                 {
2721                         ItemSpec specs[9];
2722                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2723                         specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2724                         specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2725                         specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2726                         specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2727                         specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2728                         specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2729                         if(checkItemCombination(items, specs))
2730                         {
2731                                 rlist->addItem(new MapBlockObjectItem("Sign"));
2732                                 found = true;
2733                         }
2734                 }
2735
2736                 // Torch
2737                 if(!found)
2738                 {
2739                         ItemSpec specs[9];
2740                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COALSTONE);
2741                         specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
2742                         if(checkItemCombination(items, specs))
2743                         {
2744                                 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
2745                                 found = true;
2746                         }
2747                 }
2748
2749                 // Wooden pick
2750                 if(!found)
2751                 {
2752                         ItemSpec specs[9];
2753                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2754                         specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2755                         specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
2756                         specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2757                         specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2758                         if(checkItemCombination(items, specs))
2759                         {
2760                                 rlist->addItem(new ToolItem("WPick", 0));
2761                                 found = true;
2762                         }
2763                 }
2764
2765                 // Stone pick
2766                 if(!found)
2767                 {
2768                         ItemSpec specs[9];
2769                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2770                         specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2771                         specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_STONE);
2772                         specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2773                         specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2774                         if(checkItemCombination(items, specs))
2775                         {
2776                                 rlist->addItem(new ToolItem("STPick", 0));
2777                                 found = true;
2778                         }
2779                 }
2780
2781                 // Mese pick
2782                 if(!found)
2783                 {
2784                         ItemSpec specs[9];
2785                         specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2786                         specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2787                         specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
2788                         specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
2789                         specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
2790                         if(checkItemCombination(items, specs))
2791                         {
2792                                 rlist->addItem(new ToolItem("MesePick", 0));
2793                                 found = true;
2794                         }
2795                 }
2796
2797         }
2798
2799         /*
2800                 Serialize it
2801         */
2802
2803         std::ostringstream os;
2804         //os.imbue(std::locale("C"));
2805
2806         player->inventory.serialize(os);
2807
2808         std::string s = os.str();
2809         
2810         SharedBuffer<u8> data(s.size()+2);
2811         writeU16(&data[0], TOCLIENT_INVENTORY);
2812         memcpy(&data[2], s.c_str(), s.size());
2813         
2814         // Send as reliable
2815         m_con.Send(peer_id, 0, data, true);
2816 }
2817
2818 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
2819 {
2820         DSTACK(__FUNCTION_NAME);
2821         
2822         std::ostringstream os(std::ios_base::binary);
2823         u8 buf[12];
2824         
2825         // Write command
2826         writeU16(buf, TOCLIENT_CHAT_MESSAGE);
2827         os.write((char*)buf, 2);
2828         
2829         // Write length
2830         writeU16(buf, message.size());
2831         os.write((char*)buf, 2);
2832         
2833         // Write string
2834         for(u32 i=0; i<message.size(); i++)
2835         {
2836                 u16 w = message[i];
2837                 writeU16(buf, w);
2838                 os.write((char*)buf, 2);
2839         }
2840         
2841         // Make data buffer
2842         std::string s = os.str();
2843         SharedBuffer<u8> data((u8*)s.c_str(), s.size());
2844         // Send as reliable
2845         m_con.Send(peer_id, 0, data, true);
2846 }
2847
2848 void Server::BroadcastChatMessage(const std::wstring &message)
2849 {
2850         for(core::map<u16, RemoteClient*>::Iterator
2851                 i = m_clients.getIterator();
2852                 i.atEnd() == false; i++)
2853         {
2854                 // Get client and check that it is valid
2855                 RemoteClient *client = i.getNode()->getValue();
2856                 assert(client->peer_id == i.getNode()->getKey());
2857                 if(client->serialization_version == SER_FMT_VER_INVALID)
2858                         continue;
2859
2860                 SendChatMessage(client->peer_id, message);
2861         }
2862 }
2863
2864 void Server::SendBlocks(float dtime)
2865 {
2866         DSTACK(__FUNCTION_NAME);
2867
2868         JMutexAutoLock envlock(m_env_mutex);
2869
2870         core::array<PrioritySortedBlockTransfer> queue;
2871
2872         s32 total_sending = 0;
2873
2874         for(core::map<u16, RemoteClient*>::Iterator
2875                 i = m_clients.getIterator();
2876                 i.atEnd() == false; i++)
2877         {
2878                 RemoteClient *client = i.getNode()->getValue();
2879                 assert(client->peer_id == i.getNode()->getKey());
2880
2881                 total_sending += client->SendingCount();
2882                 
2883                 if(client->serialization_version == SER_FMT_VER_INVALID)
2884                         continue;
2885                 
2886                 client->GetNextBlocks(this, dtime, queue);
2887         }
2888
2889         // Sort.
2890         // Lowest priority number comes first.
2891         // Lowest is most important.
2892         queue.sort();
2893
2894         JMutexAutoLock conlock(m_con_mutex);
2895
2896         for(u32 i=0; i<queue.size(); i++)
2897         {
2898                 //TODO: Calculate limit dynamically
2899                 if(total_sending >= g_settings.getS32
2900                                 ("max_simultaneous_block_sends_server_total"))
2901                         break;
2902                 
2903                 PrioritySortedBlockTransfer q = queue[i];
2904
2905                 MapBlock *block = NULL;
2906                 try
2907                 {
2908                         block = m_env.getMap().getBlockNoCreate(q.pos);
2909                 }
2910                 catch(InvalidPositionException &e)
2911                 {
2912                         continue;
2913                 }
2914
2915                 RemoteClient *client = getClient(q.peer_id);
2916
2917                 SendBlockNoLock(q.peer_id, block, client->serialization_version);
2918
2919                 client->SentBlock(q.pos);
2920
2921                 total_sending++;
2922         }
2923 }
2924
2925
2926 RemoteClient* Server::getClient(u16 peer_id)
2927 {
2928         DSTACK(__FUNCTION_NAME);
2929         //JMutexAutoLock lock(m_con_mutex);
2930         core::map<u16, RemoteClient*>::Node *n;
2931         n = m_clients.find(peer_id);
2932         // A client should exist for all peers
2933         assert(n != NULL);
2934         return n->getValue();
2935 }
2936
2937 void Server::UpdateBlockWaterPressure(MapBlock *block,
2938                         core::map<v3s16, MapBlock*> &modified_blocks)
2939 {
2940         MapVoxelManipulator v(&m_env.getMap());
2941         v.m_disable_water_climb =
2942                         g_settings.getBool("disable_water_climb");
2943         
2944         VoxelArea area(block->getPosRelative(),
2945                         block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
2946
2947         try
2948         {
2949                 v.updateAreaWaterPressure(area, m_flow_active_nodes);
2950         }
2951         catch(ProcessingLimitException &e)
2952         {
2953                 dstream<<"Processing limit reached (1)"<<std::endl;
2954         }
2955         
2956         v.blitBack(modified_blocks);
2957 }
2958
2959 void Server::handlePeerChange(PeerChange &c)
2960 {
2961         JMutexAutoLock envlock(m_env_mutex);
2962         JMutexAutoLock conlock(m_con_mutex);
2963         
2964         if(c.type == PEER_ADDED)
2965         {
2966                 /*
2967                         Add
2968                 */
2969
2970                 // Error check
2971                 core::map<u16, RemoteClient*>::Node *n;
2972                 n = m_clients.find(c.peer_id);
2973                 // The client shouldn't already exist
2974                 assert(n == NULL);
2975
2976                 // Create client
2977                 RemoteClient *client = new RemoteClient();
2978                 client->peer_id = c.peer_id;
2979                 m_clients.insert(client->peer_id, client);
2980
2981                 // Create player
2982                 {
2983                         Player *player = m_env.getPlayer(c.peer_id);
2984                         
2985                         // The player shouldn't already exist
2986                         assert(player == NULL);
2987
2988                         player = new ServerRemotePlayer();
2989                         player->peer_id = c.peer_id;
2990
2991                         /*
2992                                 Set player position
2993                         */
2994                         
2995                         // We're going to throw the player to this position
2996                         //v2s16 nodepos(29990,29990);
2997                         //v2s16 nodepos(9990,9990);
2998                         v2s16 nodepos(0,0);
2999                         v2s16 sectorpos = getNodeSectorPos(nodepos);
3000                         // Get zero sector (it could have been unloaded to disk)
3001                         m_env.getMap().emergeSector(sectorpos);
3002                         // Get ground height at origin
3003                         f32 groundheight = m_env.getMap().getGroundHeight(nodepos, true);
3004                         // The sector should have been generated -> groundheight exists
3005                         assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
3006                         // Don't go underwater
3007                         if(groundheight < WATER_LEVEL)
3008                                 groundheight = WATER_LEVEL;
3009
3010                         player->setPosition(intToFloat(v3s16(
3011                                         nodepos.X,
3012                                         groundheight + 1,
3013                                         nodepos.Y
3014                         )));
3015
3016                         /*
3017                                 Add player to environment
3018                         */
3019
3020                         m_env.addPlayer(player);
3021
3022                         /*
3023                                 Add stuff to inventory
3024                         */
3025                         
3026                         if(g_settings.getBool("creative_mode"))
3027                         {
3028                                 // Give a good pick
3029                                 {
3030                                         InventoryItem *item = new ToolItem("STPick", 32000);
3031                                         void* r = player->inventory.addItem("main", item);
3032                                         assert(r == NULL);
3033                                 }
3034                                 // Give all materials
3035                                 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
3036                                 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
3037                                 {
3038                                         // Skip some materials
3039                                         if(i == CONTENT_OCEAN)
3040                                                 continue;
3041
3042                                         InventoryItem *item = new MaterialItem(i, 1);
3043                                         player->inventory.addItem("main", item);
3044                                 }
3045                                 // Sign
3046                                 {
3047                                         InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3048                                         void* r = player->inventory.addItem("main", item);
3049                                         assert(r == NULL);
3050                                 }
3051                                 /*// Rat
3052                                 {
3053                                         InventoryItem *item = new MapBlockObjectItem("Rat");
3054                                         bool r = player->inventory.addItem("main", item);
3055                                         assert(r == true);
3056                                 }*/
3057                         }
3058                         else
3059                         {
3060                                 /*{
3061                                         InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
3062                                         void* r = player->inventory.addItem("main", item);
3063                                         assert(r == NULL);
3064                                 }
3065                                 {
3066                                         InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
3067                                         void* r = player->inventory.addItem("main", item);
3068                                         assert(r == NULL);
3069                                 }
3070                                 {
3071                                         InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
3072                                         void* r = player->inventory.addItem("main", item);
3073                                         assert(r == NULL);
3074                                 }
3075                                 {
3076                                         InventoryItem *item = new CraftItem("Stick", 4);
3077                                         void* r = player->inventory.addItem("main", item);
3078                                         assert(r == NULL);
3079                                 }
3080                                 {
3081                                         InventoryItem *item = new ToolItem("WPick", 32000);
3082                                         void* r = player->inventory.addItem("main", item);
3083                                         assert(r == NULL);
3084                                 }
3085                                 {
3086                                         InventoryItem *item = new ToolItem("STPick", 32000);
3087                                         void* r = player->inventory.addItem("main", item);
3088                                         assert(r == NULL);
3089                                 }*/
3090                                 /*// Give some lights
3091                                 {
3092                                         InventoryItem *item = new MaterialItem(CONTENT_TORCH, 999);
3093                                         bool r = player->inventory.addItem("main", item);
3094                                         assert(r == true);
3095                                 }
3096                                 // and some signs
3097                                 for(u16 i=0; i<4; i++)
3098                                 {
3099                                         InventoryItem *item = new MapBlockObjectItem("Sign Example text");
3100                                         bool r = player->inventory.addItem("main", item);
3101                                         assert(r == true);
3102                                 }*/
3103                                 /*// Give some other stuff
3104                                 {
3105                                         InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
3106                                         bool r = player->inventory.addItem("main", item);
3107                                         assert(r == true);
3108                                 }*/
3109                         }
3110                 }
3111
3112         } // PEER_ADDED
3113         else if(c.type == PEER_REMOVED)
3114         {
3115                 /*
3116                         Delete
3117                 */
3118
3119                 // Error check
3120                 core::map<u16, RemoteClient*>::Node *n;
3121                 n = m_clients.find(c.peer_id);
3122                 // The client should exist
3123                 assert(n != NULL);
3124                 
3125                 // Collect information about leaving in chat
3126                 std::wstring message;
3127                 {
3128                         std::wstring name = L"unknown";
3129                         Player *player = m_env.getPlayer(c.peer_id);
3130                         if(player != NULL)
3131                                 name = narrow_to_wide(player->getName());
3132                         
3133                         message += L"*** ";
3134                         message += name;
3135                         message += L" left game";
3136                 }
3137
3138                 // Delete player
3139                 {
3140                         m_env.removePlayer(c.peer_id);
3141                 }
3142                 
3143                 // Delete client
3144                 delete m_clients[c.peer_id];
3145                 m_clients.remove(c.peer_id);
3146
3147                 // Send player info to all remaining clients
3148                 SendPlayerInfos();
3149                 
3150                 // Send leave chat message to all remaining clients
3151                 BroadcastChatMessage(message);
3152                 
3153         } // PEER_REMOVED
3154         else
3155         {
3156                 assert(0);
3157         }
3158 }
3159
3160 void Server::handlePeerChanges()
3161 {
3162         while(m_peer_change_queue.size() > 0)
3163         {
3164                 PeerChange c = m_peer_change_queue.pop_front();
3165
3166                 dout_server<<"Server: Handling peer change: "
3167                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
3168                                 <<std::endl;
3169
3170                 handlePeerChange(c);
3171         }
3172 }
3173
3174