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