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