]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_sao.cpp
840e04ed99f520824c7c790a15b6e439462c8cb9
[dragonfireclient.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "content_sao.h"
21 #include "util/mathconstants.h"
22 #include "util/serialize.h"
23 #include "collision.h"
24 #include "environment.h"
25 #include "tool.h" // For ToolCapabilities
26 #include "gamedef.h"
27 #include "nodedef.h"
28 #include "remoteplayer.h"
29 #include "server.h"
30 #include "scripting_game.h"
31 #include "genericobject.h"
32
33 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
34
35 /*
36         TestSAO
37 */
38
39 class TestSAO : public ServerActiveObject
40 {
41 public:
42         TestSAO(ServerEnvironment *env, v3f pos):
43                 ServerActiveObject(env, pos),
44                 m_timer1(0),
45                 m_age(0)
46         {
47                 ServerActiveObject::registerType(getType(), create);
48         }
49         ActiveObjectType getType() const
50         { return ACTIVEOBJECT_TYPE_TEST; }
51
52         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
53                         const std::string &data)
54         {
55                 return new TestSAO(env, pos);
56         }
57
58         void step(float dtime, bool send_recommended)
59         {
60                 m_age += dtime;
61                 if(m_age > 10)
62                 {
63                         m_removed = true;
64                         return;
65                 }
66
67                 m_base_position.Y += dtime * BS * 2;
68                 if(m_base_position.Y > 8*BS)
69                         m_base_position.Y = 2*BS;
70
71                 if(send_recommended == false)
72                         return;
73
74                 m_timer1 -= dtime;
75                 if(m_timer1 < 0.0)
76                 {
77                         m_timer1 += 0.125;
78
79                         std::string data;
80
81                         data += itos(0); // 0 = position
82                         data += " ";
83                         data += itos(m_base_position.X);
84                         data += " ";
85                         data += itos(m_base_position.Y);
86                         data += " ";
87                         data += itos(m_base_position.Z);
88
89                         ActiveObjectMessage aom(getId(), false, data);
90                         m_messages_out.push(aom);
91                 }
92         }
93
94         bool getCollisionBox(aabb3f *toset) const { return false; }
95         bool collideWithObjects() const { return false; }
96
97 private:
98         float m_timer1;
99         float m_age;
100 };
101
102 // Prototype (registers item for deserialization)
103 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
104
105 /*
106         UnitSAO
107  */
108
109 UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos):
110         ServerActiveObject(env, pos),
111         m_hp(-1),
112         m_yaw(0),
113         m_properties_sent(true),
114         m_armor_groups_sent(false),
115         m_animation_range(0,0),
116         m_animation_speed(0),
117         m_animation_blend(0),
118         m_animation_loop(true),
119         m_animation_sent(false),
120         m_bone_position_sent(false),
121         m_attachment_parent_id(0),
122         m_attachment_sent(false)
123 {
124         // Initialize something to armor groups
125         m_armor_groups["fleshy"] = 100;
126 }
127
128 bool UnitSAO::isAttached() const
129 {
130         if (!m_attachment_parent_id)
131                 return false;
132         // Check if the parent still exists
133         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
134         if (obj)
135                 return true;
136         return false;
137 }
138
139 void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups)
140 {
141         m_armor_groups = armor_groups;
142         m_armor_groups_sent = false;
143 }
144
145 const ItemGroupList &UnitSAO::getArmorGroups()
146 {
147         return m_armor_groups;
148 }
149
150 void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
151 {
152         // store these so they can be updated to clients
153         m_animation_range = frame_range;
154         m_animation_speed = frame_speed;
155         m_animation_blend = frame_blend;
156         m_animation_loop = frame_loop;
157         m_animation_sent = false;
158 }
159
160 void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
161 {
162         *frame_range = m_animation_range;
163         *frame_speed = m_animation_speed;
164         *frame_blend = m_animation_blend;
165         *frame_loop = m_animation_loop;
166 }
167
168 void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
169 {
170         // store these so they can be updated to clients
171         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
172         m_bone_position_sent = false;
173 }
174
175 void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
176 {
177         *position = m_bone_position[bone].X;
178         *rotation = m_bone_position[bone].Y;
179 }
180
181 void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
182 {
183         // Attachments need to be handled on both the server and client.
184         // If we just attach on the server, we can only copy the position of the parent. Attachments
185         // are still sent to clients at an interval so players might see them lagging, plus we can't
186         // read and attach to skeletal bones.
187         // If we just attach on the client, the server still sees the child at its original location.
188         // This breaks some things so we also give the server the most accurate representation
189         // even if players only see the client changes.
190
191         m_attachment_parent_id = parent_id;
192         m_attachment_bone = bone;
193         m_attachment_position = position;
194         m_attachment_rotation = rotation;
195         m_attachment_sent = false;
196 }
197
198 void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
199         v3f *rotation)
200 {
201         *parent_id = m_attachment_parent_id;
202         *bone = m_attachment_bone;
203         *position = m_attachment_position;
204         *rotation = m_attachment_rotation;
205 }
206
207 void UnitSAO::addAttachmentChild(int child_id)
208 {
209         m_attachment_child_ids.insert(child_id);
210 }
211
212 void UnitSAO::removeAttachmentChild(int child_id)
213 {
214         m_attachment_child_ids.erase(child_id);
215 }
216
217 const UNORDERED_SET<int> &UnitSAO::getAttachmentChildIds()
218 {
219         return m_attachment_child_ids;
220 }
221
222 ObjectProperties* UnitSAO::accessObjectProperties()
223 {
224         return &m_prop;
225 }
226
227 void UnitSAO::notifyObjectPropertiesModified()
228 {
229         m_properties_sent = false;
230 }
231
232 /*
233         LuaEntitySAO
234 */
235
236 // Prototype (registers item for deserialization)
237 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
238
239 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
240                 const std::string &name, const std::string &state):
241         UnitSAO(env, pos),
242         m_init_name(name),
243         m_init_state(state),
244         m_registered(false),
245         m_velocity(0,0,0),
246         m_acceleration(0,0,0),
247         m_last_sent_yaw(0),
248         m_last_sent_position(0,0,0),
249         m_last_sent_velocity(0,0,0),
250         m_last_sent_position_timer(0),
251         m_last_sent_move_precision(0)
252 {
253         // Only register type if no environment supplied
254         if(env == NULL){
255                 ServerActiveObject::registerType(getType(), create);
256                 return;
257         }
258 }
259
260 LuaEntitySAO::~LuaEntitySAO()
261 {
262         if(m_registered){
263                 m_env->getScriptIface()->luaentity_Remove(m_id);
264         }
265
266         for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
267                 it != m_attached_particle_spawners.end(); ++it) {
268                 m_env->deleteParticleSpawner(*it, false);
269         }
270 }
271
272 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
273 {
274         ServerActiveObject::addedToEnvironment(dtime_s);
275
276         // Create entity from name
277         m_registered = m_env->getScriptIface()->
278                 luaentity_Add(m_id, m_init_name.c_str());
279
280         if(m_registered){
281                 // Get properties
282                 m_env->getScriptIface()->
283                         luaentity_GetProperties(m_id, &m_prop);
284                 // Initialize HP from properties
285                 m_hp = m_prop.hp_max;
286                 // Activate entity, supplying serialized state
287                 m_env->getScriptIface()->
288                         luaentity_Activate(m_id, m_init_state.c_str(), dtime_s);
289         } else {
290                 m_prop.infotext = m_init_name;
291         }
292 }
293
294 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
295                 const std::string &data)
296 {
297         std::string name;
298         std::string state;
299         s16 hp = 1;
300         v3f velocity;
301         float yaw = 0;
302         if(data != ""){
303                 std::istringstream is(data, std::ios::binary);
304                 // read version
305                 u8 version = readU8(is);
306                 // check if version is supported
307                 if(version == 0){
308                         name = deSerializeString(is);
309                         state = deSerializeLongString(is);
310                 }
311                 else if(version == 1){
312                         name = deSerializeString(is);
313                         state = deSerializeLongString(is);
314                         hp = readS16(is);
315                         velocity = readV3F1000(is);
316                         yaw = readF1000(is);
317                 }
318         }
319         // create object
320         infostream<<"LuaEntitySAO::create(name=\""<<name<<"\" state=\""
321                         <<state<<"\")"<<std::endl;
322         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
323         sao->m_hp = hp;
324         sao->m_velocity = velocity;
325         sao->m_yaw = yaw;
326         return sao;
327 }
328
329 void LuaEntitySAO::step(float dtime, bool send_recommended)
330 {
331         if(!m_properties_sent)
332         {
333                 m_properties_sent = true;
334                 std::string str = getPropertyPacket();
335                 // create message and add to list
336                 ActiveObjectMessage aom(getId(), true, str);
337                 m_messages_out.push(aom);
338         }
339
340         // If attached, check that our parent is still there. If it isn't, detach.
341         if(m_attachment_parent_id && !isAttached())
342         {
343                 m_attachment_parent_id = 0;
344                 m_attachment_bone = "";
345                 m_attachment_position = v3f(0,0,0);
346                 m_attachment_rotation = v3f(0,0,0);
347                 sendPosition(false, true);
348         }
349
350         m_last_sent_position_timer += dtime;
351
352         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
353         // If the object gets detached this comes into effect automatically from the last known origin
354         if(isAttached())
355         {
356                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
357                 m_base_position = pos;
358                 m_velocity = v3f(0,0,0);
359                 m_acceleration = v3f(0,0,0);
360         }
361         else
362         {
363                 if(m_prop.physical){
364                         aabb3f box = m_prop.collisionbox;
365                         box.MinEdge *= BS;
366                         box.MaxEdge *= BS;
367                         collisionMoveResult moveresult;
368                         f32 pos_max_d = BS*0.25; // Distance per iteration
369                         v3f p_pos = m_base_position;
370                         v3f p_velocity = m_velocity;
371                         v3f p_acceleration = m_acceleration;
372                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
373                                         pos_max_d, box, m_prop.stepheight, dtime,
374                                         &p_pos, &p_velocity, p_acceleration,
375                                         this, m_prop.collideWithObjects);
376
377                         // Apply results
378                         m_base_position = p_pos;
379                         m_velocity = p_velocity;
380                         m_acceleration = p_acceleration;
381                 } else {
382                         m_base_position += dtime * m_velocity + 0.5 * dtime
383                                         * dtime * m_acceleration;
384                         m_velocity += dtime * m_acceleration;
385                 }
386
387                 if((m_prop.automatic_face_movement_dir) &&
388                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
389                 {
390                         float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
391                                         + m_prop.automatic_face_movement_dir_offset;
392                         float max_rotation_delta =
393                                         dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
394
395                         if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
396                                 (fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
397
398                                 m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
399                         } else {
400                                 m_yaw = optimal_yaw;
401                         }
402                 }
403         }
404
405         if(m_registered){
406                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
407         }
408
409         if(send_recommended == false)
410                 return;
411
412         if(!isAttached())
413         {
414                 // TODO: force send when acceleration changes enough?
415                 float minchange = 0.2*BS;
416                 if(m_last_sent_position_timer > 1.0){
417                         minchange = 0.01*BS;
418                 } else if(m_last_sent_position_timer > 0.2){
419                         minchange = 0.05*BS;
420                 }
421                 float move_d = m_base_position.getDistanceFrom(m_last_sent_position);
422                 move_d += m_last_sent_move_precision;
423                 float vel_d = m_velocity.getDistanceFrom(m_last_sent_velocity);
424                 if(move_d > minchange || vel_d > minchange ||
425                                 fabs(m_yaw - m_last_sent_yaw) > 1.0){
426                         sendPosition(true, false);
427                 }
428         }
429
430         if(m_armor_groups_sent == false){
431                 m_armor_groups_sent = true;
432                 std::string str = gob_cmd_update_armor_groups(
433                                 m_armor_groups);
434                 // create message and add to list
435                 ActiveObjectMessage aom(getId(), true, str);
436                 m_messages_out.push(aom);
437         }
438
439         if(m_animation_sent == false){
440                 m_animation_sent = true;
441                 std::string str = gob_cmd_update_animation(
442                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
443                 // create message and add to list
444                 ActiveObjectMessage aom(getId(), true, str);
445                 m_messages_out.push(aom);
446         }
447
448         if(m_bone_position_sent == false){
449                 m_bone_position_sent = true;
450                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
451                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
452                         std::string str = gob_cmd_update_bone_position((*ii).first,
453                                         (*ii).second.X, (*ii).second.Y);
454                         // create message and add to list
455                         ActiveObjectMessage aom(getId(), true, str);
456                         m_messages_out.push(aom);
457                 }
458         }
459
460         if(m_attachment_sent == false){
461                 m_attachment_sent = true;
462                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
463                 // create message and add to list
464                 ActiveObjectMessage aom(getId(), true, str);
465                 m_messages_out.push(aom);
466         }
467 }
468
469 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
470 {
471         std::ostringstream os(std::ios::binary);
472
473         if(protocol_version >= 14)
474         {
475                 writeU8(os, 1); // version
476                 os<<serializeString(""); // name
477                 writeU8(os, 0); // is_player
478                 writeS16(os, getId()); //id
479                 writeV3F1000(os, m_base_position);
480                 writeF1000(os, m_yaw);
481                 writeS16(os, m_hp);
482
483                 std::ostringstream msg_os(std::ios::binary);
484                 msg_os << serializeLongString(getPropertyPacket()); // message 1
485                 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
486                 msg_os << serializeLongString(gob_cmd_update_animation(
487                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
488                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
489                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
490                         msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
491                                         (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
492                 }
493                 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
494                         m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
495                 int message_count = 4 + m_bone_position.size();
496                 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
497                                 (ii != m_attachment_child_ids.end()); ++ii) {
498                         if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
499                                 message_count++;
500                                 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
501                                         obj->getClientInitializationData(protocol_version)));
502                         }
503                 }
504
505                 writeU8(os, message_count);
506                 os.write(msg_os.str().c_str(), msg_os.str().size());
507         }
508         else
509         {
510                 writeU8(os, 0); // version
511                 os<<serializeString(""); // name
512                 writeU8(os, 0); // is_player
513                 writeV3F1000(os, m_base_position);
514                 writeF1000(os, m_yaw);
515                 writeS16(os, m_hp);
516                 writeU8(os, 2); // number of messages stuffed in here
517                 os<<serializeLongString(getPropertyPacket()); // message 1
518                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
519         }
520
521         // return result
522         return os.str();
523 }
524
525 void LuaEntitySAO::getStaticData(std::string *result) const
526 {
527         verbosestream<<FUNCTION_NAME<<std::endl;
528         std::ostringstream os(std::ios::binary);
529         // version
530         writeU8(os, 1);
531         // name
532         os<<serializeString(m_init_name);
533         // state
534         if(m_registered){
535                 std::string state = m_env->getScriptIface()->
536                         luaentity_GetStaticdata(m_id);
537                 os<<serializeLongString(state);
538         } else {
539                 os<<serializeLongString(m_init_state);
540         }
541         // hp
542         writeS16(os, m_hp);
543         // velocity
544         writeV3F1000(os, m_velocity);
545         // yaw
546         writeF1000(os, m_yaw);
547         *result = os.str();
548 }
549
550 int LuaEntitySAO::punch(v3f dir,
551                 const ToolCapabilities *toolcap,
552                 ServerActiveObject *puncher,
553                 float time_from_last_punch)
554 {
555         if (!m_registered){
556                 // Delete unknown LuaEntities when punched
557                 m_removed = true;
558                 return 0;
559         }
560
561         // It's best that attachments cannot be punched
562         if (isAttached())
563                 return 0;
564
565         ItemStack *punchitem = NULL;
566         ItemStack punchitem_static;
567         if (puncher) {
568                 punchitem_static = puncher->getWieldedItem();
569                 punchitem = &punchitem_static;
570         }
571
572         PunchDamageResult result = getPunchDamage(
573                         m_armor_groups,
574                         toolcap,
575                         punchitem,
576                         time_from_last_punch);
577
578         if (result.did_punch) {
579                 setHP(getHP() - result.damage);
580
581                 if (result.damage > 0) {
582                         std::string punchername = puncher ? puncher->getDescription() : "nil";
583
584                         actionstream << getDescription() << " punched by "
585                                         << punchername << ", damage " << result.damage
586                                         << " hp, health now " << getHP() << " hp" << std::endl;
587                 }
588
589                 std::string str = gob_cmd_punched(result.damage, getHP());
590                 // create message and add to list
591                 ActiveObjectMessage aom(getId(), true, str);
592                 m_messages_out.push(aom);
593         }
594
595         if (getHP() == 0)
596                 m_removed = true;
597
598         m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
599                         time_from_last_punch, toolcap, dir);
600
601         return result.wear;
602 }
603
604 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
605 {
606         if (!m_registered)
607                 return;
608         // It's best that attachments cannot be clicked
609         if (isAttached())
610                 return;
611         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
612 }
613
614 void LuaEntitySAO::setPos(const v3f &pos)
615 {
616         if(isAttached())
617                 return;
618         m_base_position = pos;
619         sendPosition(false, true);
620 }
621
622 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
623 {
624         if(isAttached())
625                 return;
626         m_base_position = pos;
627         if(!continuous)
628                 sendPosition(true, true);
629 }
630
631 float LuaEntitySAO::getMinimumSavedMovement()
632 {
633         return 0.1 * BS;
634 }
635
636 std::string LuaEntitySAO::getDescription()
637 {
638         std::ostringstream os(std::ios::binary);
639         os<<"LuaEntitySAO at (";
640         os<<(m_base_position.X/BS)<<",";
641         os<<(m_base_position.Y/BS)<<",";
642         os<<(m_base_position.Z/BS);
643         os<<")";
644         return os.str();
645 }
646
647 void LuaEntitySAO::setHP(s16 hp)
648 {
649         if(hp < 0) hp = 0;
650         m_hp = hp;
651 }
652
653 s16 LuaEntitySAO::getHP() const
654 {
655         return m_hp;
656 }
657
658 void LuaEntitySAO::setVelocity(v3f velocity)
659 {
660         m_velocity = velocity;
661 }
662
663 v3f LuaEntitySAO::getVelocity()
664 {
665         return m_velocity;
666 }
667
668 void LuaEntitySAO::setAcceleration(v3f acceleration)
669 {
670         m_acceleration = acceleration;
671 }
672
673 v3f LuaEntitySAO::getAcceleration()
674 {
675         return m_acceleration;
676 }
677
678 void LuaEntitySAO::setTextureMod(const std::string &mod)
679 {
680         std::string str = gob_cmd_set_texture_mod(mod);
681         // create message and add to list
682         ActiveObjectMessage aom(getId(), true, str);
683         m_messages_out.push(aom);
684 }
685
686 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
687                 bool select_horiz_by_yawpitch)
688 {
689         std::string str = gob_cmd_set_sprite(
690                 p,
691                 num_frames,
692                 framelength,
693                 select_horiz_by_yawpitch
694         );
695         // create message and add to list
696         ActiveObjectMessage aom(getId(), true, str);
697         m_messages_out.push(aom);
698 }
699
700 std::string LuaEntitySAO::getName()
701 {
702         return m_init_name;
703 }
704
705 std::string LuaEntitySAO::getPropertyPacket()
706 {
707         return gob_cmd_set_properties(m_prop);
708 }
709
710 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
711 {
712         // If the object is attached client-side, don't waste bandwidth sending its position to clients
713         if(isAttached())
714                 return;
715
716         m_last_sent_move_precision = m_base_position.getDistanceFrom(
717                         m_last_sent_position);
718         m_last_sent_position_timer = 0;
719         m_last_sent_yaw = m_yaw;
720         m_last_sent_position = m_base_position;
721         m_last_sent_velocity = m_velocity;
722         //m_last_sent_acceleration = m_acceleration;
723
724         float update_interval = m_env->getSendRecommendedInterval();
725
726         std::string str = gob_cmd_update_position(
727                 m_base_position,
728                 m_velocity,
729                 m_acceleration,
730                 m_yaw,
731                 do_interpolate,
732                 is_movement_end,
733                 update_interval
734         );
735         // create message and add to list
736         ActiveObjectMessage aom(getId(), false, str);
737         m_messages_out.push(aom);
738 }
739
740 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
741 {
742         if (m_prop.physical)
743         {
744                 //update collision box
745                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
746                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
747
748                 toset->MinEdge += m_base_position;
749                 toset->MaxEdge += m_base_position;
750
751                 return true;
752         }
753
754         return false;
755 }
756
757 bool LuaEntitySAO::collideWithObjects() const
758 {
759         return m_prop.collideWithObjects;
760 }
761
762 /*
763         PlayerSAO
764 */
765
766 // No prototype, PlayerSAO does not need to be deserialized
767
768 PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer):
769         UnitSAO(env_, v3f(0,0,0)),
770         m_player(NULL),
771         m_peer_id(peer_id_),
772         m_inventory(NULL),
773         m_damage(0),
774         m_last_good_position(0,0,0),
775         m_time_from_last_punch(0),
776         m_nocheat_dig_pos(32767, 32767, 32767),
777         m_nocheat_dig_time(0),
778         m_wield_index(0),
779         m_position_not_sent(false),
780         m_is_singleplayer(is_singleplayer),
781         m_breath(PLAYER_MAX_BREATH),
782         m_pitch(0),
783         m_fov(0),
784         m_wanted_range(0),
785         // public
786         m_physics_override_speed(1),
787         m_physics_override_jump(1),
788         m_physics_override_gravity(1),
789         m_physics_override_sneak(true),
790         m_physics_override_sneak_glitch(true),
791         m_physics_override_sent(false)
792 {
793         assert(m_peer_id != 0); // pre-condition
794
795         m_prop.hp_max = PLAYER_MAX_HP;
796         m_prop.physical = false;
797         m_prop.weight = 75;
798         m_prop.collisionbox = aabb3f(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
799         // start of default appearance, this should be overwritten by LUA
800         m_prop.visual = "upright_sprite";
801         m_prop.visual_size = v2f(1, 2);
802         m_prop.textures.clear();
803         m_prop.textures.push_back("player.png");
804         m_prop.textures.push_back("player_back.png");
805         m_prop.colors.clear();
806         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
807         m_prop.spritediv = v2s16(1,1);
808         // end of default appearance
809         m_prop.is_visible = true;
810         m_prop.makes_footstep_sound = true;
811         m_hp = PLAYER_MAX_HP;
812 }
813
814 PlayerSAO::~PlayerSAO()
815 {
816         if(m_inventory != &m_player->inventory)
817                 delete m_inventory;
818 }
819
820 void PlayerSAO::initialize(RemotePlayer *player, const std::set<std::string> &privs)
821 {
822         assert(player);
823         m_player = player;
824         m_privs = privs;
825         m_inventory = &m_player->inventory;
826 }
827
828 v3f PlayerSAO::getEyeOffset() const
829 {
830         return v3f(0, BS * 1.625f, 0);
831 }
832
833 std::string PlayerSAO::getDescription()
834 {
835         return std::string("player ") + m_player->getName();
836 }
837
838 // Called after id has been set and has been inserted in environment
839 void PlayerSAO::addedToEnvironment(u32 dtime_s)
840 {
841         ServerActiveObject::addedToEnvironment(dtime_s);
842         ServerActiveObject::setBasePosition(m_base_position);
843         m_player->setPlayerSAO(this);
844         m_player->peer_id = m_peer_id;
845         m_last_good_position = m_base_position;
846 }
847
848 // Called before removing from environment
849 void PlayerSAO::removingFromEnvironment()
850 {
851         ServerActiveObject::removingFromEnvironment();
852         if (m_player->getPlayerSAO() == this) {
853                 unlinkPlayerSessionAndSave();
854                 for (UNORDERED_SET<u32>::iterator it = m_attached_particle_spawners.begin();
855                         it != m_attached_particle_spawners.end(); ++it) {
856                         m_env->deleteParticleSpawner(*it, false);
857                 }
858         }
859 }
860
861 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
862 {
863         std::ostringstream os(std::ios::binary);
864
865         if(protocol_version >= 15)
866         {
867                 writeU8(os, 1); // version
868                 os<<serializeString(m_player->getName()); // name
869                 writeU8(os, 1); // is_player
870                 writeS16(os, getId()); //id
871                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
872                 writeF1000(os, m_yaw);
873                 writeS16(os, getHP());
874
875                 std::ostringstream msg_os(std::ios::binary);
876                 msg_os << serializeLongString(getPropertyPacket()); // message 1
877                 msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
878                 msg_os << serializeLongString(gob_cmd_update_animation(
879                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
880                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
881                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
882                         msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first,
883                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
884                 }
885                 msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id,
886                         m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
887                 msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
888                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
889                                 m_physics_override_sneak_glitch)); // 5
890                 // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
891                 msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6
892                 int message_count = 6 + m_bone_position.size();
893                 for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin();
894                                 ii != m_attachment_child_ids.end(); ++ii) {
895                         if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
896                                 message_count++;
897                                 msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(),
898                                         obj->getClientInitializationData(protocol_version)));
899                         }
900                 }
901
902                 writeU8(os, message_count);
903                 os.write(msg_os.str().c_str(), msg_os.str().size());
904         }
905         else
906         {
907                 writeU8(os, 0); // version
908                 os<<serializeString(m_player->getName()); // name
909                 writeU8(os, 1); // is_player
910                 writeV3F1000(os, m_base_position + v3f(0,BS*1,0));
911                 writeF1000(os, m_yaw);
912                 writeS16(os, getHP());
913                 writeU8(os, 2); // number of messages stuffed in here
914                 os<<serializeLongString(getPropertyPacket()); // message 1
915                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
916         }
917
918         // return result
919         return os.str();
920 }
921
922 void PlayerSAO::getStaticData(std::string *result) const
923 {
924         FATAL_ERROR("Deprecated function");
925 }
926
927 void PlayerSAO::step(float dtime, bool send_recommended)
928 {
929         if (m_drowning_interval.step(dtime, 2.0)) {
930                 // get head position
931                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
932                 MapNode n = m_env->getMap().getNodeNoEx(p);
933                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
934                 // If node generates drown
935                 if (c.drowning > 0) {
936                         if (m_hp > 0 && m_breath > 0)
937                                 setBreath(m_breath - 1);
938
939                         // No more breath, damage player
940                         if (m_breath == 0) {
941                                 setHP(m_hp - c.drowning);
942                                 m_env->getGameDef()->SendPlayerHPOrDie(this);
943                         }
944                 }
945         }
946
947         if (m_breathing_interval.step(dtime, 0.5)) {
948                 // get head position
949                 v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS);
950                 MapNode n = m_env->getMap().getNodeNoEx(p);
951                 const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n);
952                 // If player is alive & no drowning, breath
953                 if (m_hp > 0 && c.drowning == 0)
954                         setBreath(m_breath + 1);
955         }
956
957         if (!m_properties_sent) {
958                 m_properties_sent = true;
959                 std::string str = getPropertyPacket();
960                 // create message and add to list
961                 ActiveObjectMessage aom(getId(), true, str);
962                 m_messages_out.push(aom);
963         }
964
965         // If attached, check that our parent is still there. If it isn't, detach.
966         if(m_attachment_parent_id && !isAttached())
967         {
968                 m_attachment_parent_id = 0;
969                 m_attachment_bone = "";
970                 m_attachment_position = v3f(0,0,0);
971                 m_attachment_rotation = v3f(0,0,0);
972                 setBasePosition(m_last_good_position);
973                 m_env->getGameDef()->SendMovePlayer(m_peer_id);
974         }
975
976         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
977
978         // Set lag pool maximums based on estimated lag
979         const float LAG_POOL_MIN = 5.0;
980         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
981         if(lag_pool_max < LAG_POOL_MIN)
982                 lag_pool_max = LAG_POOL_MIN;
983         m_dig_pool.setMax(lag_pool_max);
984         m_move_pool.setMax(lag_pool_max);
985
986         // Increment cheat prevention timers
987         m_dig_pool.add(dtime);
988         m_move_pool.add(dtime);
989         m_time_from_last_punch += dtime;
990         m_nocheat_dig_time += dtime;
991
992         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
993         // If the object gets detached this comes into effect automatically from the last known origin
994         if (isAttached()) {
995                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
996                 m_last_good_position = pos;
997                 setBasePosition(pos);
998         }
999
1000         if (!send_recommended)
1001                 return;
1002
1003         // If the object is attached client-side, don't waste bandwidth sending its position to clients
1004         if(m_position_not_sent && !isAttached())
1005         {
1006                 m_position_not_sent = false;
1007                 float update_interval = m_env->getSendRecommendedInterval();
1008                 v3f pos;
1009                 if(isAttached()) // Just in case we ever do send attachment position too
1010                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
1011                 else
1012                         pos = m_base_position + v3f(0,BS*1,0);
1013                 std::string str = gob_cmd_update_position(
1014                         pos,
1015                         v3f(0,0,0),
1016                         v3f(0,0,0),
1017                         m_yaw,
1018                         true,
1019                         false,
1020                         update_interval
1021                 );
1022                 // create message and add to list
1023                 ActiveObjectMessage aom(getId(), false, str);
1024                 m_messages_out.push(aom);
1025         }
1026
1027         if (!m_armor_groups_sent) {
1028                 m_armor_groups_sent = true;
1029                 std::string str = gob_cmd_update_armor_groups(
1030                                 m_armor_groups);
1031                 // create message and add to list
1032                 ActiveObjectMessage aom(getId(), true, str);
1033                 m_messages_out.push(aom);
1034         }
1035
1036         if (!m_physics_override_sent) {
1037                 m_physics_override_sent = true;
1038                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
1039                                 m_physics_override_jump, m_physics_override_gravity,
1040                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
1041                 // create message and add to list
1042                 ActiveObjectMessage aom(getId(), true, str);
1043                 m_messages_out.push(aom);
1044         }
1045
1046         if (!m_animation_sent) {
1047                 m_animation_sent = true;
1048                 std::string str = gob_cmd_update_animation(
1049                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
1050                 // create message and add to list
1051                 ActiveObjectMessage aom(getId(), true, str);
1052                 m_messages_out.push(aom);
1053         }
1054
1055         if (!m_bone_position_sent) {
1056                 m_bone_position_sent = true;
1057                 for (UNORDERED_MAP<std::string, core::vector2d<v3f> >::const_iterator
1058                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
1059                         std::string str = gob_cmd_update_bone_position((*ii).first,
1060                                         (*ii).second.X, (*ii).second.Y);
1061                         // create message and add to list
1062                         ActiveObjectMessage aom(getId(), true, str);
1063                         m_messages_out.push(aom);
1064                 }
1065         }
1066
1067         if (!m_attachment_sent){
1068                 m_attachment_sent = true;
1069                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id,
1070                                 m_attachment_bone, m_attachment_position, m_attachment_rotation);
1071                 // create message and add to list
1072                 ActiveObjectMessage aom(getId(), true, str);
1073                 m_messages_out.push(aom);
1074         }
1075 }
1076
1077 void PlayerSAO::setBasePosition(const v3f &position)
1078 {
1079         if (m_player && position != m_base_position)
1080                 m_player->setDirty(true);
1081
1082         // This needs to be ran for attachments too
1083         ServerActiveObject::setBasePosition(position);
1084         m_position_not_sent = true;
1085 }
1086
1087 void PlayerSAO::setPos(const v3f &pos)
1088 {
1089         if(isAttached())
1090                 return;
1091
1092         setBasePosition(pos);
1093         // Movement caused by this command is always valid
1094         m_last_good_position = pos;
1095         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1096 }
1097
1098 void PlayerSAO::moveTo(v3f pos, bool continuous)
1099 {
1100         if(isAttached())
1101                 return;
1102
1103         setBasePosition(pos);
1104         // Movement caused by this command is always valid
1105         m_last_good_position = pos;
1106         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1107 }
1108
1109 void PlayerSAO::setYaw(const float yaw)
1110 {
1111         if (m_player && yaw != m_yaw)
1112                 m_player->setDirty(true);
1113
1114         UnitSAO::setYaw(yaw);
1115 }
1116
1117 void PlayerSAO::setFov(const float fov)
1118 {
1119         if (m_player && fov != m_fov)
1120                 m_player->setDirty(true);
1121
1122         m_fov = fov;
1123 }
1124
1125 void PlayerSAO::setWantedRange(const s16 range)
1126 {
1127         if (m_player && range != m_wanted_range)
1128                 m_player->setDirty(true);
1129
1130         m_wanted_range = range;
1131 }
1132
1133 void PlayerSAO::setYawAndSend(const float yaw)
1134 {
1135         setYaw(yaw);
1136         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1137 }
1138
1139 void PlayerSAO::setPitch(const float pitch)
1140 {
1141         if (m_player && pitch != m_pitch)
1142                 m_player->setDirty(true);
1143
1144         m_pitch = pitch;
1145 }
1146
1147 void PlayerSAO::setPitchAndSend(const float pitch)
1148 {
1149         setPitch(pitch);
1150         m_env->getGameDef()->SendMovePlayer(m_peer_id);
1151 }
1152
1153 int PlayerSAO::punch(v3f dir,
1154         const ToolCapabilities *toolcap,
1155         ServerActiveObject *puncher,
1156         float time_from_last_punch)
1157 {
1158         // It's best that attachments cannot be punched
1159         if (isAttached())
1160                 return 0;
1161
1162         if (!toolcap)
1163                 return 0;
1164
1165         // No effect if PvP disabled
1166         if (g_settings->getBool("enable_pvp") == false) {
1167                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1168                         std::string str = gob_cmd_punched(0, getHP());
1169                         // create message and add to list
1170                         ActiveObjectMessage aom(getId(), true, str);
1171                         m_messages_out.push(aom);
1172                         return 0;
1173                 }
1174         }
1175
1176         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1177                         time_from_last_punch);
1178
1179         std::string punchername = "nil";
1180
1181         if (puncher != 0)
1182                 punchername = puncher->getDescription();
1183
1184         PlayerSAO *playersao = m_player->getPlayerSAO();
1185
1186         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1187                                 puncher, time_from_last_punch, toolcap, dir,
1188                                 hitparams.hp);
1189
1190         if (!damage_handled) {
1191                 setHP(getHP() - hitparams.hp);
1192         } else { // override client prediction
1193                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1194                         std::string str = gob_cmd_punched(0, getHP());
1195                         // create message and add to list
1196                         ActiveObjectMessage aom(getId(), true, str);
1197                         m_messages_out.push(aom);
1198                 }
1199         }
1200
1201
1202         actionstream << "Player " << m_player->getName() << " punched by "
1203                         << punchername;
1204         if (!damage_handled) {
1205                 actionstream << ", damage " << hitparams.hp << " HP";
1206         } else {
1207                 actionstream << ", damage handled by lua";
1208         }
1209         actionstream << std::endl;
1210
1211         return hitparams.wear;
1212 }
1213
1214 s16 PlayerSAO::readDamage()
1215 {
1216         s16 damage = m_damage;
1217         m_damage = 0;
1218         return damage;
1219 }
1220
1221 void PlayerSAO::setHP(s16 hp)
1222 {
1223         s16 oldhp = m_hp;
1224
1225         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp);
1226         if (hp_change == 0)
1227                 return;
1228         hp = oldhp + hp_change;
1229
1230         if (hp < 0)
1231                 hp = 0;
1232         else if (hp > PLAYER_MAX_HP)
1233                 hp = PLAYER_MAX_HP;
1234
1235         if (hp < oldhp && !g_settings->getBool("enable_damage")) {
1236                 return;
1237         }
1238
1239         m_hp = hp;
1240
1241         if (oldhp > hp)
1242                 m_damage += (oldhp - hp);
1243
1244         // Update properties on death
1245         if ((hp == 0) != (oldhp == 0))
1246                 m_properties_sent = false;
1247 }
1248
1249 void PlayerSAO::setBreath(const u16 breath, bool send)
1250 {
1251         if (m_player && breath != m_breath)
1252                 m_player->setDirty(true);
1253
1254         m_breath = MYMIN(breath, PLAYER_MAX_BREATH);
1255
1256         if (send)
1257                 m_env->getGameDef()->SendPlayerBreath(this);
1258 }
1259
1260 Inventory* PlayerSAO::getInventory()
1261 {
1262         return m_inventory;
1263 }
1264 const Inventory* PlayerSAO::getInventory() const
1265 {
1266         return m_inventory;
1267 }
1268
1269 InventoryLocation PlayerSAO::getInventoryLocation() const
1270 {
1271         InventoryLocation loc;
1272         loc.setPlayer(m_player->getName());
1273         return loc;
1274 }
1275
1276 std::string PlayerSAO::getWieldList() const
1277 {
1278         return "main";
1279 }
1280
1281 ItemStack PlayerSAO::getWieldedItem() const
1282 {
1283         const Inventory *inv = getInventory();
1284         ItemStack ret;
1285         const InventoryList *mlist = inv->getList(getWieldList());
1286         if (mlist && getWieldIndex() < (s32)mlist->getSize())
1287                 ret = mlist->getItem(getWieldIndex());
1288         if (ret.name.empty()) {
1289                 const InventoryList *hlist = inv->getList("hand");
1290                 if (hlist)
1291                         ret = hlist->getItem(0);
1292         }
1293         return ret;
1294 }
1295
1296 bool PlayerSAO::setWieldedItem(const ItemStack &item)
1297 {
1298         Inventory *inv = getInventory();
1299         if (inv) {
1300                 InventoryList *mlist = inv->getList(getWieldList());
1301                 if (mlist) {
1302                         ItemStack olditem = mlist->getItem(getWieldIndex());
1303                         if (olditem.name.empty()) {
1304                                 InventoryList *hlist = inv->getList("hand");
1305                                 if (hlist) {
1306                                         hlist->changeItem(0, item);
1307                                         return true;
1308                                 }
1309                         }
1310                         mlist->changeItem(getWieldIndex(), item);
1311                         return true;
1312                 }
1313         }
1314         return false;
1315 }
1316
1317 int PlayerSAO::getWieldIndex() const
1318 {
1319         return m_wield_index;
1320 }
1321
1322 void PlayerSAO::setWieldIndex(int i)
1323 {
1324         if(i != m_wield_index) {
1325                 m_wield_index = i;
1326         }
1327 }
1328
1329 // Erase the peer id and make the object for removal
1330 void PlayerSAO::disconnected()
1331 {
1332         m_peer_id = 0;
1333         m_removed = true;
1334 }
1335
1336 void PlayerSAO::unlinkPlayerSessionAndSave()
1337 {
1338         assert(m_player->getPlayerSAO() == this);
1339         m_player->peer_id = 0;
1340         m_env->savePlayer(m_player);
1341         m_player->setPlayerSAO(NULL);
1342         m_env->removePlayer(m_player);
1343 }
1344
1345 std::string PlayerSAO::getPropertyPacket()
1346 {
1347         m_prop.is_visible = (true);
1348         return gob_cmd_set_properties(m_prop);
1349 }
1350
1351 bool PlayerSAO::checkMovementCheat()
1352 {
1353         if (isAttached() || m_is_singleplayer ||
1354                         g_settings->getBool("disable_anticheat")) {
1355                 m_last_good_position = m_base_position;
1356                 return false;
1357         }
1358
1359         bool cheated = false;
1360         /*
1361                 Check player movements
1362
1363                 NOTE: Actually the server should handle player physics like the
1364                 client does and compare player's position to what is calculated
1365                 on our side. This is required when eg. players fly due to an
1366                 explosion. Altough a node-based alternative might be possible
1367                 too, and much more lightweight.
1368         */
1369
1370         float player_max_speed = 0;
1371
1372         if (m_privs.count("fast") != 0) {
1373                 // Fast speed
1374                 player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
1375         } else {
1376                 // Normal speed
1377                 player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
1378         }
1379         // Tolerance. The lag pool does this a bit.
1380         //player_max_speed *= 2.5;
1381
1382         v3f diff = (m_base_position - m_last_good_position);
1383         float d_vert = diff.Y;
1384         diff.Y = 0;
1385         float d_horiz = diff.getLength();
1386         float required_time = d_horiz / player_max_speed;
1387
1388         if (d_vert > 0 && d_vert / player_max_speed > required_time)
1389                 required_time = d_vert / player_max_speed; // Moving upwards
1390
1391         if (m_move_pool.grab(required_time)) {
1392                 m_last_good_position = m_base_position;
1393         } else {
1394                 actionstream << "Player " << m_player->getName()
1395                                 << " moved too fast; resetting position"
1396                                 << std::endl;
1397                 setBasePosition(m_last_good_position);
1398                 cheated = true;
1399         }
1400         return cheated;
1401 }
1402
1403 bool PlayerSAO::getCollisionBox(aabb3f *toset) const
1404 {
1405         *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30);
1406         toset->MinEdge += m_base_position;
1407         toset->MaxEdge += m_base_position;
1408         return true;
1409 }