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