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