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