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