3a28e2250eef5c602de77f51706262e8627be15a
[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
25 /*
26         MapBlockObject
27 */
28
29 // This is here because it uses the MapBlock
30 v3f MapBlockObject::getAbsolutePos()
31 {
32         if(m_block == NULL)
33                 return m_pos;
34         
35         // getPosRelative gets nodepos relative to map origin
36         v3f blockpos = intToFloat(m_block->getPosRelative());
37         return blockpos + m_pos;
38 }
39
40 void MapBlockObject::setBlockChanged()
41 {
42         if(m_block)
43                 m_block->setChangedFlag();
44 }
45
46 /*
47         MovingObject
48 */
49 void MovingObject::move(float dtime, v3f acceleration)
50 {
51         DSTACK("%s: typeid=%i, pos=(%f,%f,%f), speed=(%f,%f,%f)"
52                         ", dtime=%f, acc=(%f,%f,%f)",
53                         __FUNCTION_NAME,
54                         getTypeId(),
55                         m_pos.X, m_pos.Y, m_pos.Z,
56                         m_speed.X, m_speed.Y, m_speed.Z,
57                         dtime,
58                         acceleration.X, acceleration.Y, acceleration.Z
59                         );
60         
61         v3s16 oldpos_i = floatToInt(m_pos);
62         
63         if(m_block->isValidPosition(oldpos_i) == false)
64         {
65                 // Should have wrapped, cancelling further movement.
66                 return;
67         }
68
69         // No collisions if there is no collision box
70         if(m_collision_box == NULL)
71         {
72                 m_speed += dtime * acceleration;
73                 m_pos += m_speed * dtime;
74                 return;
75         }
76         
77         // Set insane speed to zero
78         // Otherwise there will be divides by zero and other silly stuff
79         if(m_speed.getLength() > 1000.0*BS)
80                 m_speed = v3f(0,0,0);
81                 
82         // Limit speed to a reasonable value
83         float speed_limit = 20.0*BS;
84         if(m_speed.getLength() > speed_limit)
85                 m_speed = m_speed * (speed_limit / m_speed.getLength());
86
87         v3f position = m_pos;
88         v3f oldpos = position;
89
90         /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<","
91                         <<oldpos_i.Z<<")"<<std::endl;*/
92
93         // Maximum time increment (for collision detection etc)
94         // Allow 0.1 blocks per increment
95         // time = distance / speed
96         // NOTE: In the loop below collisions are detected at 0.15*BS radius
97         float speedlength = m_speed.getLength();
98         f32 dtime_max_increment;
99         if(fabs(speedlength) > 0.001)
100                 dtime_max_increment = 0.1*BS / speedlength;
101         else
102                 dtime_max_increment = 0.5;
103         
104         m_touching_ground = false;
105                 
106         u32 loopcount = 0;
107         do
108         {
109                 loopcount++;
110
111                 f32 dtime_part;
112                 if(dtime > dtime_max_increment)
113                         dtime_part = dtime_max_increment;
114                 else
115                         dtime_part = dtime;
116                 dtime -= dtime_part;
117
118                 // Begin of dtime limited code
119                 
120                 m_speed += acceleration * dtime_part;
121                 position += m_speed * dtime_part;
122
123                 /*
124                         Collision detection
125                 */
126                 
127                 v3s16 pos_i = floatToInt(position);
128                 
129                 // The loop length is limited to the object moving a distance
130                 f32 d = (float)BS * 0.15;
131
132                 core::aabbox3d<f32> objectbox(
133                                 m_collision_box->MinEdge + position,
134                                 m_collision_box->MaxEdge + position
135                 );
136                 
137                 core::aabbox3d<f32> objectbox_old(
138                                 m_collision_box->MinEdge + oldpos,
139                                 m_collision_box->MaxEdge + oldpos
140                 );
141                 
142                 //TODO: Get these ranges from somewhere
143                 for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
144                 for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
145                 for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
146                 {
147                         try{
148                                 if(material_walkable(m_block->getNodeParent(v3s16(x,y,z)).d)
149                                                 == false)
150                                         continue;
151                         }
152                         catch(InvalidPositionException &e)
153                         {
154                                 // Doing nothing here will block the player from
155                                 // walking over map borders
156                         }
157
158                         core::aabbox3d<f32> nodebox = Map::getNodeBox(
159                                         v3s16(x,y,z));
160                         
161                         // See if the player is touching ground
162                         if(
163                                         fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d
164                                         && nodebox.MaxEdge.X-d > objectbox.MinEdge.X
165                                         && nodebox.MinEdge.X+d < objectbox.MaxEdge.X
166                                         && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z
167                                         && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z
168                         ){
169                                 m_touching_ground = true;
170                         }
171                         
172                         if(objectbox.intersectsWithBox(nodebox))
173                         {
174                                         
175                 v3f dirs[3] = {
176                         v3f(0,0,1), // back
177                         v3f(0,1,0), // top
178                         v3f(1,0,0), // right
179                 };
180                 for(u16 i=0; i<3; i++)
181                 {
182                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
183                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
184                         f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]);
185                         f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]);
186                         f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]);
187                         f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]);
188
189                         bool main_edge_collides = 
190                                 ((nodemax > playermin && nodemax <= playermin_old + d
191                                         && m_speed.dotProduct(dirs[i]) < 0)
192                                 ||
193                                 (nodemin < playermax && nodemin >= playermax_old - d
194                                         && m_speed.dotProduct(dirs[i]) > 0));
195
196                         bool other_edges_collide = true;
197                         for(u16 j=0; j<3; j++)
198                         {
199                                 if(j == i)
200                                         continue;
201                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
202                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
203                                 f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]);
204                                 f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]);
205                                 if(!(nodemax - d > playermin && nodemin + d < playermax))
206                                 {
207                                         other_edges_collide = false;
208                                         break;
209                                 }
210                         }
211                         
212                         if(main_edge_collides && other_edges_collide)
213                         {
214                                 m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i];
215                                 position -= position.dotProduct(dirs[i]) * dirs[i];
216                                 position += oldpos.dotProduct(dirs[i]) * dirs[i];
217                         }
218                 
219                 }
220                 
221                         } // if(objectbox.intersectsWithBox(nodebox))
222                 } // for y
223
224         } // End of dtime limited loop
225         while(dtime > 0.001);
226
227         m_pos = position;
228 }
229
230 /*
231         MapBlockObjectList
232 */
233
234 MapBlockObjectList::MapBlockObjectList(MapBlock *block):
235         m_block(block)
236 {
237         m_mutex.Init();
238 }
239
240 MapBlockObjectList::~MapBlockObjectList()
241 {
242         clear();
243 }
244
245 /*
246         The serialization format:
247         [0] u16 number of entries
248         [2] entries (id, typeId, parameters)
249 */
250
251 void MapBlockObjectList::serialize(std::ostream &os, u8 version)
252 {
253         JMutexAutoLock lock(m_mutex);
254
255         u8 buf[2];
256         writeU16(buf, m_objects.size());
257         os.write((char*)buf, 2);
258
259         for(core::map<s16, MapBlockObject*>::Iterator
260                         i = m_objects.getIterator();
261                         i.atEnd() == false; i++)
262         {
263                 i.getNode()->getValue()->serialize(os, version);
264         }
265 }
266
267 void MapBlockObjectList::update(std::istream &is, u8 version,
268                 scene::ISceneManager *smgr)
269 {
270         JMutexAutoLock lock(m_mutex);
271
272         /*
273                 Collect all existing ids to a set.
274
275                 As things are updated, they are removed from this.
276
277                 All remaining ones are deleted.
278         */
279         core::map<s16, bool> ids_to_delete;
280         for(core::map<s16, MapBlockObject*>::Iterator
281                         i = m_objects.getIterator();
282                         i.atEnd() == false; i++)
283         {
284                 ids_to_delete.insert(i.getNode()->getKey(), true);
285         }
286         
287         u8 buf[6];
288         
289         is.read((char*)buf, 2);
290         u16 count = readU16(buf);
291
292         for(u16 i=0; i<count; i++)
293         {
294                 // Read id
295                 is.read((char*)buf, 2);
296                 s16 id = readS16(buf);
297                 
298                 // Read position
299                 // stored as x1000/BS v3s16
300                 is.read((char*)buf, 6);
301                 v3s16 pos_i = readV3S16(buf);
302                 v3f pos((f32)pos_i.X/1000*BS,
303                                 (f32)pos_i.Y/1000*BS,
304                                 (f32)pos_i.Z/1000*BS);
305
306                 // Read typeId
307                 is.read((char*)buf, 2);
308                 u16 type_id = readU16(buf);
309                 
310                 bool create_new = false;
311
312                 // Find an object with the id
313                 core::map<s16, MapBlockObject*>::Node *n;
314                 n = m_objects.find(id);
315                 // If no entry is found for id
316                 if(n == NULL)
317                 {
318                         // Insert dummy pointer node
319                         m_objects.insert(id, NULL);
320                         // Get node
321                         n = m_objects.find(id);
322                         // A new object will be created at this node
323                         create_new = true;
324                 }
325                 // If type_id differs
326                 else if(n->getValue()->getTypeId() != type_id)
327                 {
328                         // Delete old object
329                         delete n->getValue();
330                         // A new object will be created at this node
331                         create_new = true;
332                 }
333
334                 MapBlockObject *obj = NULL;
335
336                 if(create_new)
337                 {
338                         /*dstream<<"MapBlockObjectList adding new object"
339                                         " id="<<id
340                                         <<std::endl;*/
341
342                         if(type_id == MAPBLOCKOBJECT_TYPE_TEST)
343                         {
344                                 // The constructors of objects shouldn't need
345                                 // any more parameters than this.
346                                 obj = new TestObject(m_block, id, pos);
347                         }
348                         else if(type_id == MAPBLOCKOBJECT_TYPE_TEST2)
349                         {
350                                 obj = new Test2Object(m_block, id, pos);
351                         }
352                         else if(type_id == MAPBLOCKOBJECT_TYPE_SIGN)
353                         {
354                                 obj = new SignObject(m_block, id, pos);
355                         }
356                         else if(type_id == MAPBLOCKOBJECT_TYPE_RAT)
357                         {
358                                 obj = new RatObject(m_block, id, pos);
359                         }
360                         else
361                         {
362                                 throw SerializationError
363                                 ("MapBlockObjectList::update(): Unknown MapBlockObject type");
364                         }
365
366                         if(smgr != NULL)
367                                 obj->addToScene(smgr);
368
369                         n->setValue(obj);
370                 }
371                 else
372                 {
373                         obj = n->getValue();
374                         obj->updatePos(pos);
375                 }
376
377                 // Now there is an object in obj.
378                 // Update it.
379                 
380                 obj->update(is, version);
381                 
382                 // Remove from deletion list
383                 if(ids_to_delete.find(id) != NULL)
384                         ids_to_delete.remove(id);
385         }
386
387         // Delete all objects whose ids_to_delete remain in ids_to_delete
388         for(core::map<s16, bool>::Iterator
389                         i = ids_to_delete.getIterator();
390                         i.atEnd() == false; i++)
391         {
392                 s16 id = i.getNode()->getKey();
393
394                 /*dstream<<"MapBlockObjectList deleting object"
395                                 " id="<<id
396                                 <<std::endl;*/
397
398                 MapBlockObject *obj = m_objects[id];
399                 obj->removeFromScene();
400                 delete obj;
401                 m_objects.remove(id);
402         }
403 }
404
405 s16 MapBlockObjectList::getFreeId() throw(ContainerFullException)
406 {
407         s16 id = 0;
408         for(;;)
409         {
410                 if(m_objects.find(id) == NULL)
411                         return id;
412                 if(id == 32767)
413                         throw ContainerFullException
414                                         ("MapBlockObjectList doesn't fit more objects");
415                 id++;
416         }
417 }
418
419 void MapBlockObjectList::add(MapBlockObject *object)
420                 throw(ContainerFullException, AlreadyExistsException)
421 {
422         if(object == NULL)
423         {
424                 dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl;
425                 return;
426         }
427
428         JMutexAutoLock lock(m_mutex);
429
430         // Create unique id if id==-1
431         if(object->m_id == -1)
432         {
433                 object->m_id = getFreeId();
434         }
435
436         if(m_objects.find(object->m_id) != NULL)
437         {
438                 dstream<<"MapBlockObjectList::add(): "
439                                 "object with same id already exists"<<std::endl;
440                 throw AlreadyExistsException
441                                 ("MapBlockObjectList already has given id");
442         }
443         
444         object->m_block = m_block;
445         
446         /*v3f p = object->m_pos;
447         dstream<<"MapBlockObjectList::add(): "
448                         <<"m_block->getPos()=("
449                         <<m_block->getPos().X<<","
450                         <<m_block->getPos().Y<<","
451                         <<m_block->getPos().Z<<")"
452                         <<" inserting object with id="<<object->m_id
453                         <<" pos="
454                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
455                         <<std::endl;*/
456         
457         m_objects.insert(object->m_id, object);
458 }
459
460 void MapBlockObjectList::clear()
461 {
462         JMutexAutoLock lock(m_mutex);
463
464         for(core::map<s16, MapBlockObject*>::Iterator
465                         i = m_objects.getIterator();
466                         i.atEnd() == false; i++)
467         {
468                 MapBlockObject *obj = i.getNode()->getValue();
469                 //FIXME: This really shouldn't be NULL at any time,
470                 //       but this condition was added because it was.
471                 if(obj != NULL)
472                 {
473                         obj->removeFromScene();
474                         delete obj;
475                 }
476         }
477
478         m_objects.clear();
479 }
480
481 void MapBlockObjectList::remove(s16 id)
482 {
483         JMutexAutoLock lock(m_mutex);
484
485         core::map<s16, MapBlockObject*>::Node *n;
486         n = m_objects.find(id);
487         if(n == NULL)
488                 return;
489         
490         n->getValue()->removeFromScene();
491         delete n->getValue();
492         m_objects.remove(id);
493 }
494
495 MapBlockObject * MapBlockObjectList::get(s16 id)
496 {
497         core::map<s16, MapBlockObject*>::Node *n;
498         n = m_objects.find(id);
499         if(n == NULL)
500                 return NULL;
501         else
502                 return n->getValue();
503 }
504
505 void MapBlockObjectList::step(float dtime, bool server)
506 {
507         DSTACK(__FUNCTION_NAME);
508         
509         JMutexAutoLock lock(m_mutex);
510         
511         core::map<s16, bool> ids_to_delete;
512
513         {
514                 DSTACK("%s: stepping objects", __FUNCTION_NAME);
515
516                 for(core::map<s16, MapBlockObject*>::Iterator
517                                 i = m_objects.getIterator();
518                                 i.atEnd() == false; i++)
519                 {
520                         MapBlockObject *obj = i.getNode()->getValue();
521                         
522                         DSTACK("%s: stepping object type %i", __FUNCTION_NAME,
523                                         obj->getTypeId());
524
525                         if(server)
526                         {
527                                 bool to_delete = obj->serverStep(dtime);
528
529                                 if(to_delete)
530                                         ids_to_delete.insert(obj->m_id, true);
531                         }
532                         else
533                         {
534                                 obj->clientStep(dtime);
535                         }
536                 }
537         }
538
539         {
540                 DSTACK("%s: deleting objects", __FUNCTION_NAME);
541
542                 // Delete objects in delete queue
543                 for(core::map<s16, bool>::Iterator
544                                 i = ids_to_delete.getIterator();
545                                 i.atEnd() == false; i++)
546                 {
547                         s16 id = i.getNode()->getKey();
548
549                         MapBlockObject *obj = m_objects[id];
550                         obj->removeFromScene();
551                         delete obj;
552                         m_objects.remove(id);
553                 }
554         }
555         
556         /*
557                 Wrap objects on server
558         */
559
560         if(server == false)
561                 return;
562         
563         {
564                 DSTACK("%s: object wrap loop", __FUNCTION_NAME);
565
566                 for(core::map<s16, MapBlockObject*>::Iterator
567                                 i = m_objects.getIterator();
568                                 i.atEnd() == false; i++)
569                 {
570                         MapBlockObject *obj = i.getNode()->getValue();
571
572                         v3s16 pos_i = floatToInt(obj->m_pos);
573
574                         if(m_block->isValidPosition(pos_i))
575                         {
576                                 // No wrap
577                                 continue;
578                         }
579
580                         bool impossible = wrapObject(obj);
581
582                         if(impossible)
583                         {
584                                 // No wrap
585                                 continue;
586                         }
587
588                         // Restart find
589                         i = m_objects.getIterator();
590                 }
591         }
592 }
593
594 bool MapBlockObjectList::wrapObject(MapBlockObject *object)
595 {
596         DSTACK(__FUNCTION_NAME);
597         
598         // No lock here; this is called so that the lock is already locked.
599         //JMutexAutoLock lock(m_mutex);
600
601         assert(object->m_block == m_block);
602         assert(m_objects.find(object->m_id) != NULL);
603         assert(m_objects[object->m_id] == object);
604
605         NodeContainer *parentcontainer = m_block->getParent();
606         // This will only work if the parent is the map
607         if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP)
608         {
609                 dstream<<"WARNING: Wrapping object not possible: "
610                                 "MapBlock's parent is not map"<<std::endl;
611                 return true;
612         }
613         // OK, we have the map!
614         Map *map = (Map*)parentcontainer;
615         
616         // Calculate blockpos on map
617         v3s16 oldblock_pos_i_on_map = m_block->getPosRelative();
618         v3f pos_f_on_oldblock = object->m_pos;
619         v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock);
620         v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map;
621         v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map);
622
623         // Get new block
624         MapBlock *newblock;
625         try{
626                 newblock = map->getBlockNoCreate(pos_blocks_on_map);
627         }
628         catch(InvalidPositionException &e)
629         {
630                 // Couldn't find block -> not wrapping
631                 /*dstream<<"WARNING: Wrapping object not possible: "
632                                 <<"could not find new block"
633                                 <<"("<<pos_blocks_on_map.X
634                                 <<","<<pos_blocks_on_map.Y
635                                 <<","<<pos_blocks_on_map.Z
636                                 <<")"<<std::endl;*/
637                 /*dstream<<"pos_f_on_oldblock=("
638                                 <<pos_f_on_oldblock.X<<","
639                                 <<pos_f_on_oldblock.Y<<","
640                                 <<pos_f_on_oldblock.Z<<")"
641                                 <<std::endl;*/
642                 return true;
643         }
644
645         if(newblock == m_block)
646         {
647                 dstream<<"WARNING: Wrapping object not possible: "
648                                 "newblock == oldblock"<<std::endl;
649                 return true;
650         }
651         
652         // Calculate position on new block
653         v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map);
654         v3s16 newblock_pos_i_on_map = newblock->getPosRelative();
655         v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map);
656         v3f pos_f_on_newblock = pos_f_on_oldblock
657                         - newblock_pos_f_on_map + oldblock_pos_f_on_map;
658
659         // Remove object from this block
660         m_objects.remove(object->m_id);
661         
662         // Add object to new block
663         object->m_pos = pos_f_on_newblock;
664         object->m_id = -1;
665         object->m_block = NULL;
666         newblock->addObject(object);
667
668         //dstream<<"NOTE: Wrapped object"<<std::endl;
669
670         return false;
671 }
672
673 void MapBlockObjectList::getObjects(v3f origin, f32 max_d,
674                 core::array<DistanceSortedObject> &dest)
675 {
676         for(core::map<s16, MapBlockObject*>::Iterator
677                         i = m_objects.getIterator();
678                         i.atEnd() == false; i++)
679         {
680                 MapBlockObject *obj = i.getNode()->getValue();
681
682                 f32 d = (obj->m_pos - origin).getLength();
683
684                 if(d > max_d)
685                         continue;
686
687                 DistanceSortedObject dso(obj, d);
688
689                 dest.push_back(dso);
690         }
691 }
692
693 //END