eab1e62795d4e7e5907eee383da619a8bee0bcbc
[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 void LuaEntitySAO::addAttachmentChild(int child_id)
595 {
596         m_attachment_child_ids.insert(child_id);
597 }
598
599 void LuaEntitySAO::removeAttachmentChild(int child_id)
600 {
601         m_attachment_child_ids.erase(child_id);
602 }
603
604 std::set<int> LuaEntitySAO::getAttachmentChildIds()
605 {
606         return m_attachment_child_ids;
607 }
608
609 ObjectProperties* LuaEntitySAO::accessObjectProperties()
610 {
611         return &m_prop;
612 }
613
614 void LuaEntitySAO::notifyObjectPropertiesModified()
615 {
616         m_properties_sent = false;
617 }
618
619 void LuaEntitySAO::setVelocity(v3f velocity)
620 {
621         m_velocity = velocity;
622 }
623
624 v3f LuaEntitySAO::getVelocity()
625 {
626         return m_velocity;
627 }
628
629 void LuaEntitySAO::setAcceleration(v3f acceleration)
630 {
631         m_acceleration = acceleration;
632 }
633
634 v3f LuaEntitySAO::getAcceleration()
635 {
636         return m_acceleration;
637 }
638
639 void LuaEntitySAO::setYaw(float yaw)
640 {
641         m_yaw = yaw;
642 }
643
644 float LuaEntitySAO::getYaw()
645 {
646         return m_yaw;
647 }
648
649 void LuaEntitySAO::setTextureMod(const std::string &mod)
650 {
651         std::string str = gob_cmd_set_texture_mod(mod);
652         // create message and add to list
653         ActiveObjectMessage aom(getId(), true, str);
654         m_messages_out.push(aom);
655 }
656
657 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
658                 bool select_horiz_by_yawpitch)
659 {
660         std::string str = gob_cmd_set_sprite(
661                 p,
662                 num_frames,
663                 framelength,
664                 select_horiz_by_yawpitch
665         );
666         // create message and add to list
667         ActiveObjectMessage aom(getId(), true, str);
668         m_messages_out.push(aom);
669 }
670
671 std::string LuaEntitySAO::getName()
672 {
673         return m_init_name;
674 }
675
676 std::string LuaEntitySAO::getPropertyPacket()
677 {
678         return gob_cmd_set_properties(m_prop);
679 }
680
681 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
682 {
683         // If the object is attached client-side, don't waste bandwidth sending its position to clients
684         if(isAttached())
685                 return;
686
687         m_last_sent_move_precision = m_base_position.getDistanceFrom(
688                         m_last_sent_position);
689         m_last_sent_position_timer = 0;
690         m_last_sent_yaw = m_yaw;
691         m_last_sent_position = m_base_position;
692         m_last_sent_velocity = m_velocity;
693         //m_last_sent_acceleration = m_acceleration;
694
695         float update_interval = m_env->getSendRecommendedInterval();
696
697         std::string str = gob_cmd_update_position(
698                 m_base_position,
699                 m_velocity,
700                 m_acceleration,
701                 m_yaw,
702                 do_interpolate,
703                 is_movement_end,
704                 update_interval
705         );
706         // create message and add to list
707         ActiveObjectMessage aom(getId(), false, str);
708         m_messages_out.push(aom);
709 }
710
711 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
712         if (m_prop.physical)
713         {
714                 //update collision box
715                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
716                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
717
718                 toset->MinEdge += m_base_position;
719                 toset->MaxEdge += m_base_position;
720
721                 return true;
722         }
723
724         return false;
725 }
726
727 bool LuaEntitySAO::collideWithObjects(){
728         return m_prop.collideWithObjects;
729 }
730
731 /*
732         PlayerSAO
733 */
734
735 // No prototype, PlayerSAO does not need to be deserialized
736
737 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
738                 const std::set<std::string> &privs, bool is_singleplayer):
739         ServerActiveObject(env_, v3f(0,0,0)),
740         m_player(player_),
741         m_peer_id(peer_id_),
742         m_inventory(NULL),
743         m_damage(0),
744         m_last_good_position(0,0,0),
745         m_time_from_last_punch(0),
746         m_nocheat_dig_pos(32767, 32767, 32767),
747         m_nocheat_dig_time(0),
748         m_wield_index(0),
749         m_position_not_sent(false),
750         m_armor_groups_sent(false),
751         m_properties_sent(true),
752         m_privs(privs),
753         m_is_singleplayer(is_singleplayer),
754         m_animation_speed(0),
755         m_animation_blend(0),
756         m_animation_loop(true),
757         m_animation_sent(false),
758         m_bone_position_sent(false),
759         m_attachment_parent_id(0),
760         m_attachment_sent(false),
761         m_nametag_color(video::SColor(255, 255, 255, 255)),
762         m_nametag_sent(false),
763         // public
764         m_physics_override_speed(1),
765         m_physics_override_jump(1),
766         m_physics_override_gravity(1),
767         m_physics_override_sneak(true),
768         m_physics_override_sneak_glitch(true),
769         m_physics_override_sent(false)
770 {
771         assert(m_player);       // pre-condition
772         assert(m_peer_id != 0); // pre-condition
773         setBasePosition(m_player->getPosition());
774         m_inventory = &m_player->inventory;
775         m_armor_groups["fleshy"] = 100;
776
777         m_prop.hp_max = PLAYER_MAX_HP;
778         m_prop.physical = false;
779         m_prop.weight = 75;
780         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
781         // start of default appearance, this should be overwritten by LUA
782         m_prop.visual = "upright_sprite";
783         m_prop.visual_size = v2f(1, 2);
784         m_prop.textures.clear();
785         m_prop.textures.push_back("player.png");
786         m_prop.textures.push_back("player_back.png");
787         m_prop.colors.clear();
788         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
789         m_prop.spritediv = v2s16(1,1);
790         // end of default appearance
791         m_prop.is_visible = true;
792         m_prop.makes_footstep_sound = true;
793 }
794
795 PlayerSAO::~PlayerSAO()
796 {
797         if(m_inventory != &m_player->inventory)
798                 delete m_inventory;
799
800 }
801
802 std::string PlayerSAO::getDescription()
803 {
804         return std::string("player ") + m_player->getName();
805 }
806
807 // Called after id has been set and has been inserted in environment
808 void PlayerSAO::addedToEnvironment(u32 dtime_s)
809 {
810         ServerActiveObject::addedToEnvironment(dtime_s);
811         ServerActiveObject::setBasePosition(m_player->getPosition());
812         m_player->setPlayerSAO(this);
813         m_player->peer_id = m_peer_id;
814         m_last_good_position = m_player->getPosition();
815 }
816
817 // Called before removing from environment
818 void PlayerSAO::removingFromEnvironment()
819 {
820         ServerActiveObject::removingFromEnvironment();
821         if(m_player->getPlayerSAO() == this)
822         {
823                 m_player->setPlayerSAO(NULL);
824                 m_player->peer_id = 0;
825                 m_env->savePlayer(m_player->getName());
826                 m_env->removePlayer(m_player->getName());
827         }
828 }
829
830 bool PlayerSAO::isStaticAllowed() const
831 {
832         return false;
833 }
834
835 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
836 {
837         std::ostringstream os(std::ios::binary);
838
839         if(protocol_version >= 15)
840         {
841                 writeU8(os, 1); // version
842                 os<<serializeString(m_player->getName()); // name
843                 writeU8(os, 1); // is_player
844                 writeS16(os, getId()); //id
845                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
846                 writeF1000(os, m_player->getYaw());
847                 writeS16(os, getHP());
848
849                 writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
850                 os<<serializeLongString(getPropertyPacket()); // message 1
851                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
852                 os<<serializeLongString(gob_cmd_update_animation(
853                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
854                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
855                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
856                 }
857                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
858                 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
859                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
860                                 m_physics_override_sneak_glitch)); // 5
861                 os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
862         }
863         else
864         {
865                 writeU8(os, 0); // version
866                 os<<serializeString(m_player->getName()); // name
867                 writeU8(os, 1); // is_player
868                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
869                 writeF1000(os, m_player->getYaw());
870                 writeS16(os, getHP());
871                 writeU8(os, 2); // number of messages stuffed in here
872                 os<<serializeLongString(getPropertyPacket()); // message 1
873                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
874         }
875
876         // return result
877         return os.str();
878 }
879
880 std::string PlayerSAO::getStaticData()
881 {
882         FATAL_ERROR("Deprecated function (?)");
883         return "";
884 }
885
886 bool PlayerSAO::isAttached()
887 {
888         if(!m_attachment_parent_id)
889                 return false;
890         // Check if the parent still exists
891         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
892         if(obj)
893                 return true;
894         return false;
895 }
896
897 void PlayerSAO::step(float dtime, bool send_recommended)
898 {
899         if(!m_properties_sent)
900         {
901                 m_properties_sent = true;
902                 std::string str = getPropertyPacket();
903                 // create message and add to list
904                 ActiveObjectMessage aom(getId(), true, str);
905                 m_messages_out.push(aom);
906         }
907
908         // If attached, check that our parent is still there. If it isn't, detach.
909         if(m_attachment_parent_id && !isAttached())
910         {
911                 m_attachment_parent_id = 0;
912                 m_attachment_bone = "";
913                 m_attachment_position = v3f(0,0,0);
914                 m_attachment_rotation = v3f(0,0,0);
915                 m_player->setPosition(m_last_good_position);
916                 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
917         }
918
919         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
920
921         // Set lag pool maximums based on estimated lag
922         const float LAG_POOL_MIN = 5.0;
923         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
924         if(lag_pool_max < LAG_POOL_MIN)
925                 lag_pool_max = LAG_POOL_MIN;
926         m_dig_pool.setMax(lag_pool_max);
927         m_move_pool.setMax(lag_pool_max);
928
929         // Increment cheat prevention timers
930         m_dig_pool.add(dtime);
931         m_move_pool.add(dtime);
932         m_time_from_last_punch += dtime;
933         m_nocheat_dig_time += dtime;
934
935         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
936         // If the object gets detached this comes into effect automatically from the last known origin
937         if(isAttached())
938         {
939                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
940                 m_last_good_position = pos;
941                 m_player->setPosition(pos);
942         }
943
944         if(send_recommended == false)
945                 return;
946
947         // If the object is attached client-side, don't waste bandwidth sending its position to clients
948         if(m_position_not_sent && !isAttached())
949         {
950                 m_position_not_sent = false;
951                 float update_interval = m_env->getSendRecommendedInterval();
952                 v3f pos;
953                 if(isAttached()) // Just in case we ever do send attachment position too
954                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
955                 else
956                         pos = m_player->getPosition() + v3f(0,BS*1,0);
957                 std::string str = gob_cmd_update_position(
958                         pos,
959                         v3f(0,0,0),
960                         v3f(0,0,0),
961                         m_player->getYaw(),
962                         true,
963                         false,
964                         update_interval
965                 );
966                 // create message and add to list
967                 ActiveObjectMessage aom(getId(), false, str);
968                 m_messages_out.push(aom);
969         }
970
971         if(m_armor_groups_sent == false) {
972                 m_armor_groups_sent = true;
973                 std::string str = gob_cmd_update_armor_groups(
974                                 m_armor_groups);
975                 // create message and add to list
976                 ActiveObjectMessage aom(getId(), true, str);
977                 m_messages_out.push(aom);
978         }
979
980         if(m_physics_override_sent == false){
981                 m_physics_override_sent = true;
982                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
983                                 m_physics_override_jump, m_physics_override_gravity,
984                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
985                 // create message and add to list
986                 ActiveObjectMessage aom(getId(), true, str);
987                 m_messages_out.push(aom);
988         }
989
990         if(m_animation_sent == false){
991                 m_animation_sent = true;
992                 std::string str = gob_cmd_update_animation(
993                         m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
994                 // create message and add to list
995                 ActiveObjectMessage aom(getId(), true, str);
996                 m_messages_out.push(aom);
997         }
998
999         if(m_bone_position_sent == false){
1000                 m_bone_position_sent = true;
1001                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
1002                         std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
1003                         // create message and add to list
1004                         ActiveObjectMessage aom(getId(), true, str);
1005                         m_messages_out.push(aom);
1006                 }
1007         }
1008
1009         if(m_attachment_sent == false){
1010                 m_attachment_sent = true;
1011                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
1012                 // create message and add to list
1013                 ActiveObjectMessage aom(getId(), true, str);
1014                 m_messages_out.push(aom);
1015         }
1016
1017         if (m_nametag_sent == false) {
1018                 m_nametag_sent = true;
1019                 std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
1020                 // create message and add to list
1021                 ActiveObjectMessage aom(getId(), true, str);
1022                 m_messages_out.push(aom);
1023         }
1024 }
1025
1026 void PlayerSAO::setBasePosition(const v3f &position)
1027 {
1028         // This needs to be ran for attachments too
1029         ServerActiveObject::setBasePosition(position);
1030         m_position_not_sent = true;
1031 }
1032
1033 void PlayerSAO::setPos(v3f pos)
1034 {
1035         if(isAttached())
1036                 return;
1037         m_player->setPosition(pos);
1038         // Movement caused by this command is always valid
1039         m_last_good_position = pos;
1040         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1041 }
1042
1043 void PlayerSAO::moveTo(v3f pos, bool continuous)
1044 {
1045         if(isAttached())
1046                 return;
1047         m_player->setPosition(pos);
1048         // Movement caused by this command is always valid
1049         m_last_good_position = pos;
1050         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1051 }
1052
1053 void PlayerSAO::setYaw(float yaw)
1054 {
1055         m_player->setYaw(yaw);
1056         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1057 }
1058
1059 void PlayerSAO::setPitch(float pitch)
1060 {
1061         m_player->setPitch(pitch);
1062         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1063 }
1064
1065 int PlayerSAO::punch(v3f dir,
1066         const ToolCapabilities *toolcap,
1067         ServerActiveObject *puncher,
1068         float time_from_last_punch)
1069 {
1070         // It's best that attachments cannot be punched
1071         if (isAttached())
1072                 return 0;
1073
1074         if (!toolcap)
1075                 return 0;
1076
1077         // No effect if PvP disabled
1078         if (g_settings->getBool("enable_pvp") == false) {
1079                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1080                         std::string str = gob_cmd_punched(0, getHP());
1081                         // create message and add to list
1082                         ActiveObjectMessage aom(getId(), true, str);
1083                         m_messages_out.push(aom);
1084                         return 0;
1085                 }
1086         }
1087
1088         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1089                         time_from_last_punch);
1090
1091         std::string punchername = "nil";
1092
1093         if (puncher != 0)
1094                 punchername = puncher->getDescription();
1095
1096         PlayerSAO *playersao = m_player->getPlayerSAO();
1097
1098         bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
1099                                 puncher, time_from_last_punch, toolcap, dir,
1100                                 hitparams.hp);
1101
1102         if (!damage_handled) {
1103                 setHP(getHP() - hitparams.hp);
1104         } else { // override client prediction
1105                 if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1106                         std::string str = gob_cmd_punched(0, getHP());
1107                         // create message and add to list
1108                         ActiveObjectMessage aom(getId(), true, str);
1109                         m_messages_out.push(aom);
1110                 }
1111         }
1112
1113
1114         actionstream << "Player " << m_player->getName() << " punched by "
1115                         << punchername;
1116         if (!damage_handled) {
1117                 actionstream << ", damage " << hitparams.hp << " HP";
1118         } else {
1119                 actionstream << ", damage handled by lua";
1120         }
1121         actionstream << std::endl;
1122
1123         return hitparams.wear;
1124 }
1125
1126 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1127 {
1128 }
1129
1130 s16 PlayerSAO::getHP() const
1131 {
1132         return m_player->hp;
1133 }
1134
1135 s16 PlayerSAO::readDamage()
1136 {
1137         s16 damage = m_damage;
1138         m_damage = 0;
1139         return damage;
1140 }
1141
1142 void PlayerSAO::setHP(s16 hp)
1143 {
1144         s16 oldhp = m_player->hp;
1145
1146         s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
1147                 hp - oldhp);
1148         if (hp_change == 0)
1149                 return;
1150         hp = oldhp + hp_change;
1151
1152         if (hp < 0)
1153                 hp = 0;
1154         else if (hp > PLAYER_MAX_HP)
1155                 hp = PLAYER_MAX_HP;
1156
1157         if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
1158                 return;
1159         }
1160
1161         m_player->hp = hp;
1162
1163         if (oldhp > hp)
1164                 m_damage += (oldhp - hp);
1165
1166         // Update properties on death
1167         if ((hp == 0) != (oldhp == 0))
1168                 m_properties_sent = false;
1169 }
1170
1171 u16 PlayerSAO::getBreath() const
1172 {
1173         return m_player->getBreath();
1174 }
1175
1176 void PlayerSAO::setBreath(u16 breath)
1177 {
1178         m_player->setBreath(breath);
1179 }
1180
1181 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1182 {
1183         m_armor_groups = armor_groups;
1184         m_armor_groups_sent = false;
1185 }
1186
1187 ItemGroupList PlayerSAO::getArmorGroups()
1188 {
1189         return m_armor_groups;
1190 }
1191
1192 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
1193 {
1194         // store these so they can be updated to clients
1195         m_animation_range = frame_range;
1196         m_animation_speed = frame_speed;
1197         m_animation_blend = frame_blend;
1198         m_animation_loop = frame_loop;
1199         m_animation_sent = false;
1200 }
1201
1202 void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
1203 {
1204         *frame_range = m_animation_range;
1205         *frame_speed = m_animation_speed;
1206         *frame_blend = m_animation_blend;
1207         *frame_loop = m_animation_loop;
1208 }
1209
1210 void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
1211 {
1212         // store these so they can be updated to clients
1213         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1214         m_bone_position_sent = false;
1215 }
1216
1217 void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
1218 {
1219         *position = m_bone_position[bone].X;
1220         *rotation = m_bone_position[bone].Y;
1221 }
1222
1223 void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
1224 {
1225         // Attachments need to be handled on both the server and client.
1226         // If we just attach on the server, we can only copy the position of the parent. Attachments
1227         // are still sent to clients at an interval so players might see them lagging, plus we can't
1228         // read and attach to skeletal bones.
1229         // If we just attach on the client, the server still sees the child at its original location.
1230         // This breaks some things so we also give the server the most accurate representation
1231         // even if players only see the client changes.
1232
1233         m_attachment_parent_id = parent_id;
1234         m_attachment_bone = bone;
1235         m_attachment_position = position;
1236         m_attachment_rotation = rotation;
1237         m_attachment_sent = false;
1238 }
1239
1240 void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
1241         v3f *rotation)
1242 {
1243         *parent_id = m_attachment_parent_id;
1244         *bone = m_attachment_bone;
1245         *position = m_attachment_position;
1246         *rotation = m_attachment_rotation;
1247 }
1248
1249 void PlayerSAO::addAttachmentChild(int child_id)
1250 {
1251         m_attachment_child_ids.insert(child_id);
1252 }
1253
1254 void PlayerSAO::removeAttachmentChild(int child_id)
1255 {
1256         m_attachment_child_ids.erase(child_id);
1257 }
1258
1259 std::set<int> PlayerSAO::getAttachmentChildIds()
1260 {
1261         return m_attachment_child_ids;
1262 }
1263
1264 ObjectProperties* PlayerSAO::accessObjectProperties()
1265 {
1266         return &m_prop;
1267 }
1268
1269 void PlayerSAO::notifyObjectPropertiesModified()
1270 {
1271         m_properties_sent = false;
1272 }
1273
1274 void PlayerSAO::setNametagColor(video::SColor color)
1275 {
1276         m_nametag_color = color;
1277         m_nametag_sent = false;
1278 }
1279
1280 video::SColor PlayerSAO::getNametagColor()
1281 {
1282         return m_nametag_color;
1283 }
1284
1285 Inventory* PlayerSAO::getInventory()
1286 {
1287         return m_inventory;
1288 }
1289 const Inventory* PlayerSAO::getInventory() const
1290 {
1291         return m_inventory;
1292 }
1293
1294 InventoryLocation PlayerSAO::getInventoryLocation() const
1295 {
1296         InventoryLocation loc;
1297         loc.setPlayer(m_player->getName());
1298         return loc;
1299 }
1300
1301 std::string PlayerSAO::getWieldList() const
1302 {
1303         return "main";
1304 }
1305
1306 int PlayerSAO::getWieldIndex() const
1307 {
1308         return m_wield_index;
1309 }
1310
1311 void PlayerSAO::setWieldIndex(int i)
1312 {
1313         if(i != m_wield_index) {
1314                 m_wield_index = i;
1315         }
1316 }
1317
1318 void PlayerSAO::disconnected()
1319 {
1320         m_peer_id = 0;
1321         m_removed = true;
1322         if(m_player->getPlayerSAO() == this)
1323         {
1324                 m_player->setPlayerSAO(NULL);
1325                 m_player->peer_id = 0;
1326         }
1327 }
1328
1329 std::string PlayerSAO::getPropertyPacket()
1330 {
1331         m_prop.is_visible = (true);
1332         return gob_cmd_set_properties(m_prop);
1333 }
1334
1335 bool PlayerSAO::checkMovementCheat()
1336 {
1337         bool cheated = false;
1338         if(isAttached() || m_is_singleplayer ||
1339                         g_settings->getBool("disable_anticheat"))
1340         {
1341                 m_last_good_position = m_player->getPosition();
1342         }
1343         else
1344         {
1345                 /*
1346                         Check player movements
1347
1348                         NOTE: Actually the server should handle player physics like the
1349                         client does and compare player's position to what is calculated
1350                         on our side. This is required when eg. players fly due to an
1351                         explosion. Altough a node-based alternative might be possible
1352                         too, and much more lightweight.
1353                 */
1354
1355                 float player_max_speed = 0;
1356                 if(m_privs.count("fast") != 0){
1357                         // Fast speed
1358                         player_max_speed = m_player->movement_speed_fast;
1359                 } else {
1360                         // Normal speed
1361                         player_max_speed = m_player->movement_speed_walk;
1362                 }
1363                 // Tolerance. With the lag pool we shouldn't need it.
1364                 //player_max_speed *= 2.5;
1365                 //player_max_speed_up *= 2.5;
1366
1367                 v3f diff = (m_player->getPosition() - m_last_good_position);
1368                 float d_vert = diff.Y;
1369                 diff.Y = 0;
1370                 float d_horiz = diff.getLength();
1371                 float required_time = d_horiz/player_max_speed;
1372                 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1373                         required_time = d_vert/player_max_speed;
1374                 if(m_move_pool.grab(required_time)){
1375                         m_last_good_position = m_player->getPosition();
1376                 } else {
1377                         actionstream<<"Player "<<m_player->getName()
1378                                         <<" moved too fast; resetting position"
1379                                         <<std::endl;
1380                         m_player->setPosition(m_last_good_position);
1381                         cheated = true;
1382                 }
1383         }
1384         return cheated;
1385 }
1386
1387 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1388         //update collision box
1389         *toset = m_player->getCollisionbox();
1390
1391         toset->MinEdge += m_base_position;
1392         toset->MaxEdge += m_base_position;
1393
1394         return true;
1395 }
1396
1397 bool PlayerSAO::collideWithObjects(){
1398         return true;
1399 }
1400