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