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