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