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