tweaking mapgenv2 settings for maximum awesomeness.
[oweals/minetest.git] / src / mapblockobject.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblockobject.h"
21 #include "mapblock.h"
22 // Only for ::getNodeBox, TODO: Get rid of this
23 #include "map.h"
24 #include "inventory.h"
25 #include "irrlichtwrapper.h"
26
27 /*
28         MapBlockObject
29 */
30
31 // This is here because it uses the MapBlock
32 v3f MapBlockObject::getAbsolutePos()
33 {
34         if(m_block == NULL)
35                 return m_pos;
36         
37         // getPosRelative gets nodepos relative to map origin
38         v3f blockpos = intToFloat(m_block->getPosRelative());
39         return blockpos + m_pos;
40 }
41
42 void MapBlockObject::setBlockChanged()
43 {
44         if(m_block)
45                 m_block->setChangedFlag();
46 }
47
48 /*
49         MovingObject
50 */
51
52 v3f MovingObject::getAbsoluteShowPos()
53 {
54         if(m_block == NULL)
55                 return m_pos;
56         
57         // getPosRelative gets nodepos relative to map origin
58         v3f blockpos = intToFloat(m_block->getPosRelative());
59         return blockpos + m_showpos;
60 }
61
62 void MovingObject::move(float dtime, v3f acceleration)
63 {
64         DSTACK("%s: typeid=%i, pos=(%f,%f,%f), speed=(%f,%f,%f)"
65                         ", dtime=%f, acc=(%f,%f,%f)",
66                         __FUNCTION_NAME,
67                         getTypeId(),
68                         m_pos.X, m_pos.Y, m_pos.Z,
69                         m_speed.X, m_speed.Y, m_speed.Z,
70                         dtime,
71                         acceleration.X, acceleration.Y, acceleration.Z
72                         );
73         
74         v3s16 oldpos_i = floatToInt(m_pos);
75         
76         if(m_block->isValidPosition(oldpos_i) == false)
77         {
78                 // Should have wrapped, cancelling further movement.
79                 return;
80         }
81
82         // No collisions if there is no collision box
83         if(m_collision_box == NULL)
84         {
85                 m_speed += dtime * acceleration;
86                 m_pos += m_speed * dtime;
87                 return;
88         }
89         
90         // Set insane speed to zero
91         // Otherwise there will be divides by zero and other silly stuff
92         if(m_speed.getLength() > 1000.0*BS)
93                 m_speed = v3f(0,0,0);
94                 
95         // Limit speed to a reasonable value
96         float speed_limit = 20.0*BS;
97         if(m_speed.getLength() > speed_limit)
98                 m_speed = m_speed * (speed_limit / m_speed.getLength());
99
100         v3f position = m_pos;
101         v3f oldpos = position;
102
103         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
104                         <<oldpos_i.Z<<")"<<std::endl;*/
105
106         // Maximum time increment (for collision detection etc)
107         // Allow 0.1 blocks per increment
108         // time = distance / speed
109         // NOTE: In the loop below collisions are detected at 0.15*BS radius
110         float speedlength = m_speed.getLength();
111         f32 dtime_max_increment;
112         if(fabs(speedlength) > 0.001)
113                 dtime_max_increment = 0.05*BS / speedlength;
114         else
115                 dtime_max_increment = 0.5;
116         
117         m_touching_ground = false;
118                 
119         u32 loopcount = 0;
120         do
121         {
122                 loopcount++;
123
124                 f32 dtime_part;
125                 if(dtime > dtime_max_increment)
126                         dtime_part = dtime_max_increment;
127                 else
128                         dtime_part = dtime;
129                 dtime -= dtime_part;
130
131                 // Begin of dtime limited code
132                 
133                 m_speed += acceleration * dtime_part;
134                 position += m_speed * dtime_part;
135
136                 /*
137                         Collision detection
138                 */
139                 
140                 v3s16 pos_i = floatToInt(position);
141                 
142                 // The loop length is limited to the object moving a distance
143                 f32 d = (float)BS * 0.15;
144
145                 core::aabbox3d<f32> objectbox(
146                                 m_collision_box->MinEdge + position,
147                                 m_collision_box->MaxEdge + position
148                 );
149                 
150                 core::aabbox3d<f32> objectbox_old(
151                                 m_collision_box->MinEdge + oldpos,
152                                 m_collision_box->MaxEdge + oldpos
153                 );
154                 
155                 //TODO: Get these ranges from somewhere
156                 for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
157                 for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
158                 for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
159                 {
160                         try{
161                                 if(content_walkable(m_block->getNodeParent(v3s16(x,y,z)).d)
162                                                 == false)
163                                         continue;
164                         }
165                         catch(InvalidPositionException &e)
166                         {
167                                 // Doing nothing here will block the object from
168                                 // walking over map borders
169                         }
170
171                         core::aabbox3d<f32> nodebox = Map::getNodeBox(
172                                         v3s16(x,y,z));
173                         
174                         // See if the object is touching ground
175                         if(
176                                         fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d
177                                         && nodebox.MaxEdge.X-d > objectbox.MinEdge.X
178                                         && nodebox.MinEdge.X+d < objectbox.MaxEdge.X
179                                         && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z
180                                         && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z
181                         ){
182                                 m_touching_ground = true;
183                         }
184                         
185                         if(objectbox.intersectsWithBox(nodebox))
186                         {
187                                         
188                 v3f dirs[3] = {
189                         v3f(0,0,1), // back
190                         v3f(0,1,0), // top
191                         v3f(1,0,0), // right
192                 };
193                 for(u16 i=0; i<3; i++)
194                 {
195                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
196                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
197                         f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]);
198                         f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]);
199                         f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]);
200                         f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]);
201
202                         bool main_edge_collides = 
203                                 ((nodemax > playermin && nodemax <= playermin_old + d
204                                         && m_speed.dotProduct(dirs[i]) < 0)
205                                 ||
206                                 (nodemin < playermax && nodemin >= playermax_old - d
207                                         && m_speed.dotProduct(dirs[i]) > 0));
208
209                         bool other_edges_collide = true;
210                         for(u16 j=0; j<3; j++)
211                         {
212                                 if(j == i)
213                                         continue;
214                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
215                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
216                                 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]);
217                                 f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]);
218                                 if(!(nodemax - d > playermin && nodemin + d < playermax))
219                                 {
220                                         other_edges_collide = false;
221                                         break;
222                                 }
223                         }
224                         
225                         if(main_edge_collides && other_edges_collide)
226                         {
227                                 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
228                                 position -= position.dotProduct(dirs[i]) * dirs[i];
229                                 position += oldpos.dotProduct(dirs[i]) * dirs[i];
230                         }
231                 
232                 }
233                 
234                         } // if(objectbox.intersectsWithBox(nodebox))
235                 } // for y
236
237         } // End of dtime limited loop
238         while(dtime > 0.001);
239
240         m_pos = position;
241 }
242
243 void MovingObject::simpleMove(float dtime)
244 {
245         m_pos_animation_time_counter += dtime;
246         m_pos_animation_counter += dtime;
247         v3f movevector = m_pos - m_oldpos;
248         f32 moveratio;
249         if(m_pos_animation_time < 0.001)
250                 moveratio = 1.0;
251         else
252                 moveratio = m_pos_animation_counter / m_pos_animation_time;
253         if(moveratio > 1.5)
254                 moveratio = 1.5;
255         m_showpos = m_oldpos + movevector * moveratio;
256 }
257
258 #ifndef SERVER
259 /*
260         RatObject
261 */
262 void RatObject::addToScene(scene::ISceneManager *smgr)
263 {
264         if(m_node != NULL)
265                 return;
266         
267         video::IVideoDriver* driver = smgr->getVideoDriver();
268         
269         scene::SMesh *mesh = new scene::SMesh();
270         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
271         video::SColor c(255,255,255,255);
272         video::S3DVertex vertices[4] =
273         {
274                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
275                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
276                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
277                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
278         };
279         u16 indices[] = {0,1,2,2,3,0};
280         buf->append(vertices, 4, indices, 6);
281         // Set material
282         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
283         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
284         buf->getMaterial().setTexture
285                         (0, driver->getTexture(porting::getDataPath("rat.png").c_str()));
286         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
287         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
288         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
289         // Add to mesh
290         mesh->addMeshBuffer(buf);
291         buf->drop();
292         m_node = smgr->addMeshSceneNode(mesh, NULL);
293         mesh->drop();
294         updateNodePos();
295 }
296 #endif
297
298 /*
299         ItemObject
300 */
301 #ifndef SERVER
302 void ItemObject::addToScene(scene::ISceneManager *smgr)
303 {
304         if(m_node != NULL)
305                 return;
306         
307         //video::IVideoDriver* driver = smgr->getVideoDriver();
308         
309         // Get image of item for showing
310         video::ITexture *texture = getItemImage();
311
312         /*
313                 Create a mesh
314         */
315
316         scene::SMesh *mesh = new scene::SMesh();
317         {
318         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
319         video::SColor c(255,255,255,255);
320         video::S3DVertex vertices[4] =
321         {
322                 /*video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 0,1),
323                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 1,1),
324                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 1,0),
325                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 0,0),*/
326                 video::S3DVertex(BS/3,-BS/2,0, 0,0,0, c, 0,1),
327                 video::S3DVertex(-BS/3,-BS/2,0, 0,0,0, c, 1,1),
328                 video::S3DVertex(-BS/3,-BS/2+BS*2/3,0, 0,0,0, c, 1,0),
329                 video::S3DVertex(BS/3,-BS/2+BS*2/3,0, 0,0,0, c, 0,0),
330         };
331         u16 indices[] = {0,1,2,2,3,0};
332         buf->append(vertices, 4, indices, 6);
333         // Set material
334         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
335         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
336         buf->getMaterial().setTexture(0, texture);
337         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
338         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
339         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
340         // Add to mesh
341         mesh->addMeshBuffer(buf);
342         buf->drop();
343         }
344         m_node = smgr->addMeshSceneNode(mesh, NULL);
345         // Set it to use the materials of the meshbuffers directly.
346         // This is needed for changing the texture in the future
347         ((scene::IMeshSceneNode*)m_node)->setReadOnlyMaterials(true);
348         mesh->drop();
349
350         updateSceneNode();
351 }
352
353 video::ITexture * ItemObject::getItemImage()
354 {
355         /*
356                 Create an inventory item to see what is its image
357         */
358         video::ITexture *texture = NULL;
359         InventoryItem *item = createInventoryItem();
360         if(item)
361                 texture = item->getImage();
362         /*else
363                 texture = g_irrlicht->getTexture(porting::getDataPath("cloud.png").c_str());*/
364         if(item)
365                 delete item;
366         return texture;
367 }
368
369 #endif
370
371 InventoryItem * ItemObject::createInventoryItem()
372 {
373         try{
374                 std::istringstream is(m_itemstring, std::ios_base::binary);
375                 InventoryItem *item = InventoryItem::deSerialize(is);
376                 dstream<<__FUNCTION_NAME<<": m_itemstring=\""
377                                 <<m_itemstring<<"\" -> item="<<item
378                                 <<std::endl;
379                 return item;
380         }
381         catch(SerializationError &e)
382         {
383                 dstream<<__FUNCTION_NAME<<": serialization error: "
384                                 <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
385                 return NULL;
386         }
387 }
388
389 /*
390         PlayerObject
391 */
392 #ifndef SERVER
393 void PlayerObject::addToScene(scene::ISceneManager *smgr)
394 {
395         if(m_node != NULL)
396                 return;
397         
398         video::IVideoDriver* driver = smgr->getVideoDriver();
399
400         // Attach a simple mesh to the player for showing an image
401         scene::SMesh *mesh = new scene::SMesh();
402         { // Front
403         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
404         video::SColor c(255,255,255,255);
405         video::S3DVertex vertices[4] =
406         {
407                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
408                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
409                 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
410                 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
411         };
412         u16 indices[] = {0,1,2,2,3,0};
413         buf->append(vertices, 4, indices, 6);
414         // Set material
415         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
416         //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
417         buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player.png").c_str()));
418         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
419         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
420         //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
421         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
422         // Add to mesh
423         mesh->addMeshBuffer(buf);
424         buf->drop();
425         }
426         { // Back
427         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
428         video::SColor c(255,255,255,255);
429         video::S3DVertex vertices[4] =
430         {
431                 video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1),
432                 video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1),
433                 video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0),
434                 video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0),
435         };
436         u16 indices[] = {0,1,2,2,3,0};
437         buf->append(vertices, 4, indices, 6);
438         // Set material
439         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
440         //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
441         buf->getMaterial().setTexture(0, driver->getTexture(porting::getDataPath("player_back.png").c_str()));
442         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
443         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
444         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
445         // Add to mesh
446         mesh->addMeshBuffer(buf);
447         buf->drop();
448         }
449         
450         m_node = smgr->addMeshSceneNode(mesh, NULL);
451         mesh->drop();
452         updateNodePos();
453 }
454 #endif
455
456 /*
457         MapBlockObjectList
458 */
459
460 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
461         m_block(block)
462 {
463         m_mutex.Init();
464 }
465
466 MapBlockObjectList::~MapBlockObjectList()
467 {
468         clear();
469 }
470
471 /*
472         The serialization format:
473         [0] u16 number of entries
474         [2] entries (id, typeId, parameters)
475 */
476
477 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
478 {
479         JMutexAutoLock lock(m_mutex);
480
481         u8 buf[2];
482         writeU16(buf, m_objects.size());
483         os.write((char*)buf, 2);
484
485         for(core::map<s16, MapBlockObject*>::Iterator
486                         i = m_objects.getIterator();
487                         i.atEnd() == false; i++)
488         {
489                 i.getNode()->getValue()->serialize(os, version);
490         }
491 }
492
493 void MapBlockObjectList::update(std::istream &is, u8 version,
494                 scene::ISceneManager *smgr, u32 daynight_ratio)
495 {
496         JMutexAutoLock lock(m_mutex);
497
498         /*
499                 Collect all existing ids to a set.
500
501                 As things are updated, they are removed from this.
502
503                 All remaining ones are deleted.
504         */
505         core::map<s16, bool> ids_to_delete;
506         for(core::map<s16, MapBlockObject*>::Iterator
507                         i = m_objects.getIterator();
508                         i.atEnd() == false; i++)
509         {
510                 ids_to_delete.insert(i.getNode()->getKey(), true);
511         }
512         
513         u8 buf[6];
514         
515         is.read((char*)buf, 2);
516         u16 count = readU16(buf);
517
518         for(u16 i=0; i<count; i++)
519         {
520                 // Read id
521                 is.read((char*)buf, 2);
522                 s16 id = readS16(buf);
523                 
524                 // Read position
525                 // stored as x1000/BS v3s16
526                 is.read((char*)buf, 6);
527                 v3s16 pos_i = readV3S16(buf);
528                 v3f pos((f32)pos_i.X/1000*BS,
529                                 (f32)pos_i.Y/1000*BS,
530                                 (f32)pos_i.Z/1000*BS);
531
532                 // Read typeId
533                 is.read((char*)buf, 2);
534                 u16 type_id = readU16(buf);
535                 
536                 bool create_new = false;
537
538                 // Find an object with the id
539                 core::map<s16, MapBlockObject*>::Node *n;
540                 n = m_objects.find(id);
541                 // If no entry is found for id
542                 if(n == NULL)
543                 {
544                         // Insert dummy pointer node
545                         m_objects.insert(id, NULL);
546                         // Get node
547                         n = m_objects.find(id);
548                         // A new object will be created at this node
549                         create_new = true;
550                 }
551                 // If type_id differs
552                 else if(n->getValue()->getTypeId() != type_id)
553                 {
554                         // Delete old object
555                         delete n->getValue();
556                         // A new object will be created at this node
557                         create_new = true;
558                 }
559
560                 MapBlockObject *obj = NULL;
561
562                 if(create_new)
563                 {
564                         /*dstream<<"MapBlockObjectList adding new object"
565                                         " id="<<id
566                                         <<std::endl;*/
567
568                         if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
569                         {
570                                 obj = new SignObject(m_block, id, pos);
571                         }
572                         else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
573                         {
574                                 obj = new RatObject(m_block, id, pos);
575                         }
576                         else if(type_id == MAPBLOCKOBJECT_TYPE_ITEM)
577                         {
578                                 obj = new ItemObject(m_block, id, pos);
579                         }
580                         else
581                         {
582                                 // This is fatal because we cannot know the length
583                                 // of the object's data
584                                 throw SerializationError
585                                 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
586                         }
587
588                         if(smgr != NULL)
589                                 //obj->addToScene(smgr, daynight_ratio);
590                                 obj->addToScene(smgr);
591
592                         n->setValue(obj);
593                 }
594                 else
595                 {
596                         obj = n->getValue();
597                         obj->updatePos(pos);
598                         /*if(daynight_ratio != m_last_update_daynight_ratio)
599                         {
600                                 obj->removeFromScene();
601                                 obj->addToScene(smgr, daynight_ratio);
602                         }*/
603                 }
604
605                 // Now there is an object in obj.
606                 // Update it.
607                 
608                 obj->update(is, version);
609                 
610                 /*
611                         Update light on client
612                 */
613                 if(smgr != NULL)
614                 {
615                         u8 light = LIGHT_MAX;
616                         try{
617                                 v3s16 relpos_i = floatToInt(obj->m_pos);
618                                 MapNode n = m_block->getNodeParent(relpos_i);
619                                 light = n.getLightBlend(daynight_ratio);
620                         }
621                         catch(InvalidPositionException &e) {}
622                         obj->updateLight(light);
623                 }
624                 
625                 // Remove from deletion list
626                 if(ids_to_delete.find(id) != NULL)
627                         ids_to_delete.remove(id);
628         }
629
630         // Delete all objects whose ids_to_delete remain in ids_to_delete
631         for(core::map<s16, bool>::Iterator
632                         i = ids_to_delete.getIterator();
633                         i.atEnd() == false; i++)
634         {
635                 s16 id = i.getNode()->getKey();
636
637                 /*dstream<<"MapBlockObjectList deleting object"
638                                 " id="<<id
639                                 <<std::endl;*/
640
641                 MapBlockObject *obj = m_objects[id];
642                 obj->removeFromScene();
643                 delete obj;
644                 m_objects.remove(id);
645         }
646
647         m_last_update_daynight_ratio = daynight_ratio;
648 }
649
650 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
651 {
652         s16 id = 0;
653         for(;;)
654         {
655                 if(m_objects.find(id) == NULL)
656                         return id;
657                 if(id == 32767)
658                         throw ContainerFullException
659                                         ("MapBlockObjectList doesn't fit more objects");
660                 id++;
661         }
662 }
663
664 void MapBlockObjectList::add(MapBlockObject *object)
665                 throw(ContainerFullException, AlreadyExistsException)
666 {
667         if(object == NULL)
668         {
669                 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
670                 return;
671         }
672
673         JMutexAutoLock lock(m_mutex);
674
675         // Create unique id if id==-1
676         if(object->m_id == -1)
677         {
678                 object->m_id = getFreeId();
679         }
680
681         if(m_objects.find(object->m_id) != NULL)
682         {
683                 dstream<<"MapBlockObjectList::add(): "
684                                 "object with same id already exists"<<std::endl;
685                 throw AlreadyExistsException
686                                 ("MapBlockObjectList already has given id");
687         }
688         
689         object->m_block = m_block;
690         
691         /*v3f p = object->m_pos;
692         dstream<<"MapBlockObjectList::add(): "
693                         <<"m_block->getPos()=("
694                         <<m_block->getPos().X<<","
695                         <<m_block->getPos().Y<<","
696                         <<m_block->getPos().Z<<")"
697                         <<" inserting object with id="<<object->m_id
698                         <<" pos="
699                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
700                         <<std::endl;*/
701         
702         m_objects.insert(object->m_id, object);
703 }
704
705 void MapBlockObjectList::clear()
706 {
707         JMutexAutoLock lock(m_mutex);
708
709         for(core::map<s16, MapBlockObject*>::Iterator
710                         i = m_objects.getIterator();
711                         i.atEnd() == false; i++)
712         {
713                 MapBlockObject *obj = i.getNode()->getValue();
714                 //FIXME: This really shouldn't be NULL at any time,
715                 //       but this condition was added because it was.
716                 if(obj != NULL)
717                 {
718                         obj->removeFromScene();
719                         delete obj;
720                 }
721         }
722
723         m_objects.clear();
724 }
725
726 void MapBlockObjectList::remove(s16 id)
727 {
728         JMutexAutoLock lock(m_mutex);
729
730         core::map<s16, MapBlockObject*>::Node *n;
731         n = m_objects.find(id);
732         if(n == NULL)
733                 return;
734         
735         n->getValue()->removeFromScene();
736         delete n->getValue();
737         m_objects.remove(id);
738 }
739
740 MapBlockObject * MapBlockObjectList::get(s16 id)
741 {
742         core::map<s16, MapBlockObject*>::Node *n;
743         n = m_objects.find(id);
744         if(n == NULL)
745                 return NULL;
746         else
747                 return n->getValue();
748 }
749
750 void MapBlockObjectList::step(float dtime, bool server, u32 daynight_ratio)
751 {
752         DSTACK(__FUNCTION_NAME);
753         
754         JMutexAutoLock lock(m_mutex);
755         
756         core::map<s16, bool> ids_to_delete;
757
758         {
759                 DSTACK("%s: stepping objects", __FUNCTION_NAME);
760
761                 for(core::map<s16, MapBlockObject*>::Iterator
762                                 i = m_objects.getIterator();
763                                 i.atEnd() == false; i++)
764                 {
765                         MapBlockObject *obj = i.getNode()->getValue();
766                         
767                         DSTACK("%s: stepping object type %i", __FUNCTION_NAME,
768                                         obj->getTypeId());
769
770                         if(server)
771                         {
772                                 // Update light
773                                 u8 light = LIGHT_MAX;
774                                 try{
775                                         v3s16 relpos_i = floatToInt(obj->m_pos);
776                                         MapNode n = m_block->getNodeParent(relpos_i);
777                                         light = n.getLightBlend(daynight_ratio);
778                                 }
779                                 catch(InvalidPositionException &e) {}
780                                 obj->updateLight(light);
781                                 
782                                 bool to_delete = obj->serverStep(dtime, daynight_ratio);
783
784                                 if(to_delete)
785                                         ids_to_delete.insert(obj->m_id, true);
786                         }
787                         else
788                         {
789                                 obj->clientStep(dtime);
790                         }
791                 }
792         }
793
794         {
795                 DSTACK("%s: deleting objects", __FUNCTION_NAME);
796
797                 // Delete objects in delete queue
798                 for(core::map<s16, bool>::Iterator
799                                 i = ids_to_delete.getIterator();
800                                 i.atEnd() == false; i++)
801                 {
802                         s16 id = i.getNode()->getKey();
803
804                         MapBlockObject *obj = m_objects[id];
805                         obj->removeFromScene();
806                         delete obj;
807                         m_objects.remove(id);
808                 }
809         }
810         
811         /*
812                 Wrap objects on server
813         */
814
815         if(server == false)
816                 return;
817         
818         {
819                 DSTACK("%s: object wrap loop", __FUNCTION_NAME);
820
821                 for(core::map<s16, MapBlockObject*>::Iterator
822                                 i = m_objects.getIterator();
823                                 i.atEnd() == false; i++)
824                 {
825                         MapBlockObject *obj = i.getNode()->getValue();
826
827                         v3s16 pos_i = floatToInt(obj->m_pos);
828
829                         if(m_block->isValidPosition(pos_i))
830                         {
831                                 // No wrap
832                                 continue;
833                         }
834
835                         bool impossible = wrapObject(obj);
836
837                         if(impossible)
838                         {
839                                 // No wrap
840                                 continue;
841                         }
842
843                         // Restart find
844                         i = m_objects.getIterator();
845                 }
846         }
847 }
848
849 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
850 {
851         DSTACK(__FUNCTION_NAME);
852         
853         // No lock here; this is called so that the lock is already locked.
854         //JMutexAutoLock lock(m_mutex);
855
856         assert(object->m_block == m_block);
857         assert(m_objects.find(object->m_id) != NULL);
858         assert(m_objects[object->m_id] == object);
859
860         NodeContainer *parentcontainer = m_block->getParent();
861         // This will only work if the parent is the map
862         if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
863         {
864                 dstream<<"WARNING: Wrapping object not possible: "
865                                 "MapBlock's parent is not map"<<std::endl;
866                 return true;
867         }
868         // OK, we have the map!
869         Map *map = (Map*)parentcontainer;
870         
871         // Calculate blockpos on map
872         v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
873         v3f pos_f_on_oldblock = object->m_pos;
874         v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
875         v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
876         v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
877
878         // Get new block
879         MapBlock *newblock;
880         try{
881                 newblock = map->getBlockNoCreate(pos_blocks_on_map);
882         }
883         catch(InvalidPositionException &e)
884         {
885                 // Couldn't find block -> not wrapping
886                 /*dstream<<"WARNING: Wrapping object not possible: "
887                                 <<"could not find new block"
888                                 <<"("<<pos_blocks_on_map.X
889                                 <<","<<pos_blocks_on_map.Y
890                                 <<","<<pos_blocks_on_map.Z
891                                 <<")"<<std::endl;*/
892                 /*dstream<<"pos_f_on_oldblock=("
893                                 <<pos_f_on_oldblock.X<<","
894                                 <<pos_f_on_oldblock.Y<<","
895                                 <<pos_f_on_oldblock.Z<<")"
896                                 <<std::endl;*/
897                 return true;
898         }
899
900         if(newblock == m_block)
901         {
902                 dstream<<"WARNING: Wrapping object not possible: "
903                                 "newblock == oldblock"<<std::endl;
904                 return true;
905         }
906         
907         // Calculate position on new block
908         v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
909         v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
910         v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
911         v3f pos_f_on_newblock = pos_f_on_oldblock
912                         - newblock_pos_f_on_map + oldblock_pos_f_on_map;
913
914         // Remove object from this block
915         m_objects.remove(object->m_id);
916         
917         // Add object to new block
918         object->m_pos = pos_f_on_newblock;
919         object->m_id = -1;
920         object->m_block = NULL;
921         newblock->addObject(object);
922
923         //dstream<<"NOTE: Wrapped object"<<std::endl;
924
925         return false;
926 }
927
928 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
929                 core::array<DistanceSortedObject> &dest)
930 {
931         for(core::map<s16, MapBlockObject*>::Iterator
932                         i = m_objects.getIterator();
933                         i.atEnd() == false; i++)
934         {
935                 MapBlockObject *obj = i.getNode()->getValue();
936
937                 f32 d = (obj->getRelativeShowPos() - origin).getLength();
938
939                 if(d > max_d)
940                         continue;
941
942                 DistanceSortedObject dso(obj, d);
943
944                 dest.push_back(dso);
945         }
946 }
947
948 //END