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