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