For usages of assert() that are meant to persist in Release builds (when NDEBUG is...
[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 "main.h" // For g_profiler
27 #include "profiler.h"
28 #include "serialization.h" // For compressZlib
29 #include "tool.h" // For ToolCapabilities
30 #include "gamedef.h"
31 #include "player.h"
32 #include "server.h"
33 #include "scripting_game.h"
34 #include "genericobject.h"
35 #include "log.h"
36
37 std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
38
39 /*
40         TestSAO
41 */
42
43 class TestSAO : public ServerActiveObject
44 {
45 public:
46         TestSAO(ServerEnvironment *env, v3f pos):
47                 ServerActiveObject(env, pos),
48                 m_timer1(0),
49                 m_age(0)
50         {
51                 ServerActiveObject::registerType(getType(), create);
52         }
53         ActiveObjectType getType() const
54         { return ACTIVEOBJECT_TYPE_TEST; }
55
56         static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
57                         const std::string &data)
58         {
59                 return new TestSAO(env, pos);
60         }
61
62         void step(float dtime, bool send_recommended)
63         {
64                 m_age += dtime;
65                 if(m_age > 10)
66                 {
67                         m_removed = true;
68                         return;
69                 }
70
71                 m_base_position.Y += dtime * BS * 2;
72                 if(m_base_position.Y > 8*BS)
73                         m_base_position.Y = 2*BS;
74
75                 if(send_recommended == false)
76                         return;
77
78                 m_timer1 -= dtime;
79                 if(m_timer1 < 0.0)
80                 {
81                         m_timer1 += 0.125;
82
83                         std::string data;
84
85                         data += itos(0); // 0 = position
86                         data += " ";
87                         data += itos(m_base_position.X);
88                         data += " ";
89                         data += itos(m_base_position.Y);
90                         data += " ";
91                         data += itos(m_base_position.Z);
92
93                         ActiveObjectMessage aom(getId(), false, data);
94                         m_messages_out.push(aom);
95                 }
96         }
97
98         bool getCollisionBox(aabb3f *toset) {
99                 return false;
100         }
101
102         bool collideWithObjects() {
103                 return false;
104         }
105
106 private:
107         float m_timer1;
108         float m_age;
109 };
110
111 // Prototype (registers item for deserialization)
112 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
113
114 /*
115         LuaEntitySAO
116 */
117
118 // Prototype (registers item for deserialization)
119 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
120
121 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
122                 const std::string &name, const std::string &state):
123         ServerActiveObject(env, pos),
124         m_init_name(name),
125         m_init_state(state),
126         m_registered(false),
127         m_hp(-1),
128         m_velocity(0,0,0),
129         m_acceleration(0,0,0),
130         m_yaw(0),
131         m_properties_sent(true),
132         m_last_sent_yaw(0),
133         m_last_sent_position(0,0,0),
134         m_last_sent_velocity(0,0,0),
135         m_last_sent_position_timer(0),
136         m_last_sent_move_precision(0),
137         m_armor_groups_sent(false),
138         m_animation_speed(0),
139         m_animation_blend(0),
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(m_animation_range, m_animation_speed, m_animation_blend);
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(m_animation_range, m_animation_speed, m_animation_blend)); // 3
371                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
372                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
373                 }
374                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
375         }
376         else
377         {
378                 writeU8(os, 0); // version
379                 os<<serializeString(""); // name
380                 writeU8(os, 0); // is_player
381                 writeV3F1000(os, m_base_position);
382                 writeF1000(os, m_yaw);
383                 writeS16(os, m_hp);
384                 writeU8(os, 2); // number of messages stuffed in here
385                 os<<serializeLongString(getPropertyPacket()); // message 1
386                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
387         }
388
389         // return result
390         return os.str();
391 }
392
393 std::string LuaEntitySAO::getStaticData()
394 {
395         verbosestream<<__FUNCTION_NAME<<std::endl;
396         std::ostringstream os(std::ios::binary);
397         // version
398         writeU8(os, 1);
399         // name
400         os<<serializeString(m_init_name);
401         // state
402         if(m_registered){
403                 std::string state = m_env->getScriptIface()->
404                         luaentity_GetStaticdata(m_id);
405                 os<<serializeLongString(state);
406         } else {
407                 os<<serializeLongString(m_init_state);
408         }
409         // hp
410         writeS16(os, m_hp);
411         // velocity
412         writeV3F1000(os, m_velocity);
413         // yaw
414         writeF1000(os, m_yaw);
415         return os.str();
416 }
417
418 int LuaEntitySAO::punch(v3f dir,
419                 const ToolCapabilities *toolcap,
420                 ServerActiveObject *puncher,
421                 float time_from_last_punch)
422 {
423         if(!m_registered){
424                 // Delete unknown LuaEntities when punched
425                 m_removed = true;
426                 return 0;
427         }
428
429         // It's best that attachments cannot be punched
430         if(isAttached())
431                 return 0;
432
433         ItemStack *punchitem = NULL;
434         ItemStack punchitem_static;
435         if(puncher){
436                 punchitem_static = puncher->getWieldedItem();
437                 punchitem = &punchitem_static;
438         }
439
440         PunchDamageResult result = getPunchDamage(
441                         m_armor_groups,
442                         toolcap,
443                         punchitem,
444                         time_from_last_punch);
445
446         if(result.did_punch)
447         {
448                 setHP(getHP() - result.damage);
449
450
451                 std::string punchername = "nil";
452
453                 if ( puncher != 0 )
454                         punchername = puncher->getDescription();
455
456                 actionstream<<getDescription()<<" punched by "
457                                 <<punchername<<", damage "<<result.damage
458                                 <<" hp, health now "<<getHP()<<" hp"<<std::endl;
459
460                 {
461                         std::string str = gob_cmd_punched(result.damage, getHP());
462                         // create message and add to list
463                         ActiveObjectMessage aom(getId(), true, str);
464                         m_messages_out.push(aom);
465                 }
466
467                 if(getHP() == 0)
468                         m_removed = true;
469         }
470
471         m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
472                         time_from_last_punch, toolcap, dir);
473
474         return result.wear;
475 }
476
477 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
478 {
479         if(!m_registered)
480                 return;
481         // It's best that attachments cannot be clicked
482         if(isAttached())
483                 return;
484         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
485 }
486
487 void LuaEntitySAO::setPos(v3f pos)
488 {
489         if(isAttached())
490                 return;
491         m_base_position = pos;
492         sendPosition(false, true);
493 }
494
495 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
496 {
497         if(isAttached())
498                 return;
499         m_base_position = pos;
500         if(!continuous)
501                 sendPosition(true, true);
502 }
503
504 float LuaEntitySAO::getMinimumSavedMovement()
505 {
506         return 0.1 * BS;
507 }
508
509 std::string LuaEntitySAO::getDescription()
510 {
511         std::ostringstream os(std::ios::binary);
512         os<<"LuaEntitySAO at (";
513         os<<(m_base_position.X/BS)<<",";
514         os<<(m_base_position.Y/BS)<<",";
515         os<<(m_base_position.Z/BS);
516         os<<")";
517         return os.str();
518 }
519
520 void LuaEntitySAO::setHP(s16 hp)
521 {
522         if(hp < 0) hp = 0;
523         m_hp = hp;
524 }
525
526 s16 LuaEntitySAO::getHP() const
527 {
528         return m_hp;
529 }
530
531 void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
532 {
533         m_armor_groups = armor_groups;
534         m_armor_groups_sent = false;
535 }
536
537 void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
538 {
539         m_animation_range = frame_range;
540         m_animation_speed = frame_speed;
541         m_animation_blend = frame_blend;
542         m_animation_sent = false;
543 }
544
545 void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
546 {
547         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
548         m_bone_position_sent = false;
549 }
550
551 void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
552 {
553         // Attachments need to be handled on both the server and client.
554         // If we just attach on the server, we can only copy the position of the parent. Attachments
555         // are still sent to clients at an interval so players might see them lagging, plus we can't
556         // read and attach to skeletal bones.
557         // If we just attach on the client, the server still sees the child at its original location.
558         // This breaks some things so we also give the server the most accurate representation
559         // even if players only see the client changes.
560
561         m_attachment_parent_id = parent_id;
562         m_attachment_bone = bone;
563         m_attachment_position = position;
564         m_attachment_rotation = rotation;
565         m_attachment_sent = false;
566 }
567
568 ObjectProperties* LuaEntitySAO::accessObjectProperties()
569 {
570         return &m_prop;
571 }
572
573 void LuaEntitySAO::notifyObjectPropertiesModified()
574 {
575         m_properties_sent = false;
576 }
577
578 void LuaEntitySAO::setVelocity(v3f velocity)
579 {
580         m_velocity = velocity;
581 }
582
583 v3f LuaEntitySAO::getVelocity()
584 {
585         return m_velocity;
586 }
587
588 void LuaEntitySAO::setAcceleration(v3f acceleration)
589 {
590         m_acceleration = acceleration;
591 }
592
593 v3f LuaEntitySAO::getAcceleration()
594 {
595         return m_acceleration;
596 }
597
598 void LuaEntitySAO::setYaw(float yaw)
599 {
600         m_yaw = yaw;
601 }
602
603 float LuaEntitySAO::getYaw()
604 {
605         return m_yaw;
606 }
607
608 void LuaEntitySAO::setTextureMod(const std::string &mod)
609 {
610         std::string str = gob_cmd_set_texture_mod(mod);
611         // create message and add to list
612         ActiveObjectMessage aom(getId(), true, str);
613         m_messages_out.push(aom);
614 }
615
616 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
617                 bool select_horiz_by_yawpitch)
618 {
619         std::string str = gob_cmd_set_sprite(
620                 p,
621                 num_frames,
622                 framelength,
623                 select_horiz_by_yawpitch
624         );
625         // create message and add to list
626         ActiveObjectMessage aom(getId(), true, str);
627         m_messages_out.push(aom);
628 }
629
630 std::string LuaEntitySAO::getName()
631 {
632         return m_init_name;
633 }
634
635 std::string LuaEntitySAO::getPropertyPacket()
636 {
637         return gob_cmd_set_properties(m_prop);
638 }
639
640 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
641 {
642         // If the object is attached client-side, don't waste bandwidth sending its position to clients
643         if(isAttached())
644                 return;
645
646         m_last_sent_move_precision = m_base_position.getDistanceFrom(
647                         m_last_sent_position);
648         m_last_sent_position_timer = 0;
649         m_last_sent_yaw = m_yaw;
650         m_last_sent_position = m_base_position;
651         m_last_sent_velocity = m_velocity;
652         //m_last_sent_acceleration = m_acceleration;
653
654         float update_interval = m_env->getSendRecommendedInterval();
655
656         std::string str = gob_cmd_update_position(
657                 m_base_position,
658                 m_velocity,
659                 m_acceleration,
660                 m_yaw,
661                 do_interpolate,
662                 is_movement_end,
663                 update_interval
664         );
665         // create message and add to list
666         ActiveObjectMessage aom(getId(), false, str);
667         m_messages_out.push(aom);
668 }
669
670 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
671         if (m_prop.physical)
672         {
673                 //update collision box
674                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
675                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
676
677                 toset->MinEdge += m_base_position;
678                 toset->MaxEdge += m_base_position;
679
680                 return true;
681         }
682
683         return false;
684 }
685
686 bool LuaEntitySAO::collideWithObjects(){
687         return m_prop.collideWithObjects;
688 }
689
690 /*
691         PlayerSAO
692 */
693
694 // No prototype, PlayerSAO does not need to be deserialized
695
696 PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
697                 const std::set<std::string> &privs, bool is_singleplayer):
698         ServerActiveObject(env_, v3f(0,0,0)),
699         m_player(player_),
700         m_peer_id(peer_id_),
701         m_inventory(NULL),
702         m_damage(0),
703         m_last_good_position(0,0,0),
704         m_time_from_last_punch(0),
705         m_nocheat_dig_pos(32767, 32767, 32767),
706         m_nocheat_dig_time(0),
707         m_wield_index(0),
708         m_position_not_sent(false),
709         m_armor_groups_sent(false),
710         m_properties_sent(true),
711         m_privs(privs),
712         m_is_singleplayer(is_singleplayer),
713         m_animation_speed(0),
714         m_animation_blend(0),
715         m_animation_sent(false),
716         m_bone_position_sent(false),
717         m_attachment_parent_id(0),
718         m_attachment_sent(false),
719         // public
720         m_physics_override_speed(1),
721         m_physics_override_jump(1),
722         m_physics_override_gravity(1),
723         m_physics_override_sneak(true),
724         m_physics_override_sneak_glitch(true),
725         m_physics_override_sent(false)
726 {
727         assert(m_player);       // pre-condition
728         assert(m_peer_id != 0); // pre-condition
729         setBasePosition(m_player->getPosition());
730         m_inventory = &m_player->inventory;
731         m_armor_groups["fleshy"] = 100;
732
733         m_prop.hp_max = PLAYER_MAX_HP;
734         m_prop.physical = false;
735         m_prop.weight = 75;
736         m_prop.collisionbox = core::aabbox3d<f32>(-1/3.,-1.0,-1/3., 1/3.,1.0,1/3.);
737         // start of default appearance, this should be overwritten by LUA
738         m_prop.visual = "upright_sprite";
739         m_prop.visual_size = v2f(1, 2);
740         m_prop.textures.clear();
741         m_prop.textures.push_back("player.png");
742         m_prop.textures.push_back("player_back.png");
743         m_prop.colors.clear();
744         m_prop.colors.push_back(video::SColor(255, 255, 255, 255));
745         m_prop.spritediv = v2s16(1,1);
746         // end of default appearance
747         m_prop.is_visible = true;
748         m_prop.makes_footstep_sound = true;
749 }
750
751 PlayerSAO::~PlayerSAO()
752 {
753         if(m_inventory != &m_player->inventory)
754                 delete m_inventory;
755
756 }
757
758 std::string PlayerSAO::getDescription()
759 {
760         return std::string("player ") + m_player->getName();
761 }
762
763 // Called after id has been set and has been inserted in environment
764 void PlayerSAO::addedToEnvironment(u32 dtime_s)
765 {
766         ServerActiveObject::addedToEnvironment(dtime_s);
767         ServerActiveObject::setBasePosition(m_player->getPosition());
768         m_player->setPlayerSAO(this);
769         m_player->peer_id = m_peer_id;
770         m_last_good_position = m_player->getPosition();
771 }
772
773 // Called before removing from environment
774 void PlayerSAO::removingFromEnvironment()
775 {
776         ServerActiveObject::removingFromEnvironment();
777         if(m_player->getPlayerSAO() == this)
778         {
779                 m_player->setPlayerSAO(NULL);
780                 m_player->peer_id = 0;
781                 m_env->savePlayer(m_player->getName());
782                 m_env->removePlayer(m_player->getName());
783         }
784 }
785
786 bool PlayerSAO::isStaticAllowed() const
787 {
788         return false;
789 }
790
791 std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
792 {
793         std::ostringstream os(std::ios::binary);
794
795         if(protocol_version >= 15)
796         {
797                 writeU8(os, 1); // version
798                 os<<serializeString(m_player->getName()); // name
799                 writeU8(os, 1); // is_player
800                 writeS16(os, getId()); //id
801                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
802                 writeF1000(os, m_player->getYaw());
803                 writeS16(os, getHP());
804
805                 writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
806                 os<<serializeLongString(getPropertyPacket()); // message 1
807                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
808                 os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
809                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
810                         os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
811                 }
812                 os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
813                 os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
814                                 m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
815                                 m_physics_override_sneak_glitch)); // 5
816         }
817         else
818         {
819                 writeU8(os, 0); // version
820                 os<<serializeString(m_player->getName()); // name
821                 writeU8(os, 1); // is_player
822                 writeV3F1000(os, m_player->getPosition() + v3f(0,BS*1,0));
823                 writeF1000(os, m_player->getYaw());
824                 writeS16(os, getHP());
825                 writeU8(os, 2); // number of messages stuffed in here
826                 os<<serializeLongString(getPropertyPacket()); // message 1
827                 os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
828         }
829
830         // return result
831         return os.str();
832 }
833
834 std::string PlayerSAO::getStaticData()
835 {
836         FATAL_ERROR("Deprecated function (?)");
837         return "";
838 }
839
840 bool PlayerSAO::isAttached()
841 {
842         if(!m_attachment_parent_id)
843                 return false;
844         // Check if the parent still exists
845         ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id);
846         if(obj)
847                 return true;
848         return false;
849 }
850
851 void PlayerSAO::step(float dtime, bool send_recommended)
852 {
853         if(!m_properties_sent)
854         {
855                 m_properties_sent = true;
856                 std::string str = getPropertyPacket();
857                 // create message and add to list
858                 ActiveObjectMessage aom(getId(), true, str);
859                 m_messages_out.push(aom);
860         }
861
862         // If attached, check that our parent is still there. If it isn't, detach.
863         if(m_attachment_parent_id && !isAttached())
864         {
865                 m_attachment_parent_id = 0;
866                 m_attachment_bone = "";
867                 m_attachment_position = v3f(0,0,0);
868                 m_attachment_rotation = v3f(0,0,0);
869                 m_player->setPosition(m_last_good_position);
870                 ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
871         }
872
873         //dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
874
875         // Set lag pool maximums based on estimated lag
876         const float LAG_POOL_MIN = 5.0;
877         float lag_pool_max = m_env->getMaxLagEstimate() * 2.0;
878         if(lag_pool_max < LAG_POOL_MIN)
879                 lag_pool_max = LAG_POOL_MIN;
880         m_dig_pool.setMax(lag_pool_max);
881         m_move_pool.setMax(lag_pool_max);
882
883         // Increment cheat prevention timers
884         m_dig_pool.add(dtime);
885         m_move_pool.add(dtime);
886         m_time_from_last_punch += dtime;
887         m_nocheat_dig_time += dtime;
888
889         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
890         // If the object gets detached this comes into effect automatically from the last known origin
891         if(isAttached())
892         {
893                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
894                 m_last_good_position = pos;
895                 m_player->setPosition(pos);
896         }
897
898         if(send_recommended == false)
899                 return;
900
901         // If the object is attached client-side, don't waste bandwidth sending its position to clients
902         if(m_position_not_sent && !isAttached())
903         {
904                 m_position_not_sent = false;
905                 float update_interval = m_env->getSendRecommendedInterval();
906                 v3f pos;
907                 if(isAttached()) // Just in case we ever do send attachment position too
908                         pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
909                 else
910                         pos = m_player->getPosition() + v3f(0,BS*1,0);
911                 std::string str = gob_cmd_update_position(
912                         pos,
913                         v3f(0,0,0),
914                         v3f(0,0,0),
915                         m_player->getYaw(),
916                         true,
917                         false,
918                         update_interval
919                 );
920                 // create message and add to list
921                 ActiveObjectMessage aom(getId(), false, str);
922                 m_messages_out.push(aom);
923         }
924
925         if(m_armor_groups_sent == false) {
926                 m_armor_groups_sent = true;
927                 std::string str = gob_cmd_update_armor_groups(
928                                 m_armor_groups);
929                 // create message and add to list
930                 ActiveObjectMessage aom(getId(), true, str);
931                 m_messages_out.push(aom);
932         }
933
934         if(m_physics_override_sent == false){
935                 m_physics_override_sent = true;
936                 std::string str = gob_cmd_update_physics_override(m_physics_override_speed,
937                                 m_physics_override_jump, m_physics_override_gravity,
938                                 m_physics_override_sneak, m_physics_override_sneak_glitch);
939                 // create message and add to list
940                 ActiveObjectMessage aom(getId(), true, str);
941                 m_messages_out.push(aom);
942         }
943
944         if(m_animation_sent == false){
945                 m_animation_sent = true;
946                 std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
947                 // create message and add to list
948                 ActiveObjectMessage aom(getId(), true, str);
949                 m_messages_out.push(aom);
950         }
951
952         if(m_bone_position_sent == false){
953                 m_bone_position_sent = true;
954                 for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
955                         std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
956                         // create message and add to list
957                         ActiveObjectMessage aom(getId(), true, str);
958                         m_messages_out.push(aom);
959                 }
960         }
961
962         if(m_attachment_sent == false){
963                 m_attachment_sent = true;
964                 std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
965                 // create message and add to list
966                 ActiveObjectMessage aom(getId(), true, str);
967                 m_messages_out.push(aom);
968         }
969 }
970
971 void PlayerSAO::setBasePosition(const v3f &position)
972 {
973         // This needs to be ran for attachments too
974         ServerActiveObject::setBasePosition(position);
975         m_position_not_sent = true;
976 }
977
978 void PlayerSAO::setPos(v3f pos)
979 {
980         if(isAttached())
981                 return;
982         m_player->setPosition(pos);
983         // Movement caused by this command is always valid
984         m_last_good_position = pos;
985         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
986 }
987
988 void PlayerSAO::moveTo(v3f pos, bool continuous)
989 {
990         if(isAttached())
991                 return;
992         m_player->setPosition(pos);
993         // Movement caused by this command is always valid
994         m_last_good_position = pos;
995         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
996 }
997
998 void PlayerSAO::setYaw(float yaw)
999 {
1000         m_player->setYaw(yaw);
1001         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1002 }
1003
1004 void PlayerSAO::setPitch(float pitch)
1005 {
1006         m_player->setPitch(pitch);
1007         ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
1008 }
1009
1010 int PlayerSAO::punch(v3f dir,
1011         const ToolCapabilities *toolcap,
1012         ServerActiveObject *puncher,
1013         float time_from_last_punch)
1014 {
1015         // It's best that attachments cannot be punched
1016         if(isAttached())
1017                 return 0;
1018
1019         if(!toolcap)
1020                 return 0;
1021
1022         // No effect if PvP disabled
1023         if(g_settings->getBool("enable_pvp") == false){
1024                 if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
1025                         std::string str = gob_cmd_punched(0, getHP());
1026                         // create message and add to list
1027                         ActiveObjectMessage aom(getId(), true, str);
1028                         m_messages_out.push(aom);
1029                         return 0;
1030                 }
1031         }
1032
1033         HitParams hitparams = getHitParams(m_armor_groups, toolcap,
1034                         time_from_last_punch);
1035
1036         std::string punchername = "nil";
1037
1038         if ( puncher != 0 )
1039                 punchername = puncher->getDescription();
1040
1041         actionstream<<"Player "<<m_player->getName()<<" punched by "
1042                         <<punchername<<", damage "<<hitparams.hp
1043                         <<" HP"<<std::endl;
1044
1045         setHP(getHP() - hitparams.hp);
1046
1047         return hitparams.wear;
1048 }
1049
1050 void PlayerSAO::rightClick(ServerActiveObject *clicker)
1051 {
1052 }
1053
1054 s16 PlayerSAO::getHP() const
1055 {
1056         return m_player->hp;
1057 }
1058
1059 s16 PlayerSAO::readDamage()
1060 {
1061         s16 damage = m_damage;
1062         m_damage = 0;
1063         return damage;
1064 }
1065
1066 void PlayerSAO::setHP(s16 hp)
1067 {
1068         s16 oldhp = m_player->hp;
1069
1070         if (hp < 0)
1071                 hp = 0;
1072         else if (hp > PLAYER_MAX_HP)
1073                 hp = PLAYER_MAX_HP;
1074
1075         m_player->hp = hp;
1076
1077         if (oldhp > hp)
1078                 m_damage += (oldhp - hp);
1079
1080         // Update properties on death
1081         if ((hp == 0) != (oldhp == 0))
1082                 m_properties_sent = false;
1083 }
1084
1085 u16 PlayerSAO::getBreath() const
1086 {
1087         return m_player->getBreath();
1088 }
1089
1090 void PlayerSAO::setBreath(u16 breath)
1091 {
1092         m_player->setBreath(breath);
1093 }
1094
1095 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
1096 {
1097         m_armor_groups = armor_groups;
1098         m_armor_groups_sent = false;
1099 }
1100
1101 void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
1102 {
1103         // store these so they can be updated to clients
1104         m_animation_range = frame_range;
1105         m_animation_speed = frame_speed;
1106         m_animation_blend = frame_blend;
1107         m_animation_sent = false;
1108 }
1109
1110 void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
1111 {
1112         // store these so they can be updated to clients
1113         m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
1114         m_bone_position_sent = false;
1115 }
1116
1117 void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
1118 {
1119         // Attachments need to be handled on both the server and client.
1120         // If we just attach on the server, we can only copy the position of the parent. Attachments
1121         // are still sent to clients at an interval so players might see them lagging, plus we can't
1122         // read and attach to skeletal bones.
1123         // If we just attach on the client, the server still sees the child at its original location.
1124         // This breaks some things so we also give the server the most accurate representation
1125         // even if players only see the client changes.
1126
1127         m_attachment_parent_id = parent_id;
1128         m_attachment_bone = bone;
1129         m_attachment_position = position;
1130         m_attachment_rotation = rotation;
1131         m_attachment_sent = false;
1132 }
1133
1134 ObjectProperties* PlayerSAO::accessObjectProperties()
1135 {
1136         return &m_prop;
1137 }
1138
1139 void PlayerSAO::notifyObjectPropertiesModified()
1140 {
1141         m_properties_sent = false;
1142 }
1143
1144 Inventory* PlayerSAO::getInventory()
1145 {
1146         return m_inventory;
1147 }
1148 const Inventory* PlayerSAO::getInventory() const
1149 {
1150         return m_inventory;
1151 }
1152
1153 InventoryLocation PlayerSAO::getInventoryLocation() const
1154 {
1155         InventoryLocation loc;
1156         loc.setPlayer(m_player->getName());
1157         return loc;
1158 }
1159
1160 std::string PlayerSAO::getWieldList() const
1161 {
1162         return "main";
1163 }
1164
1165 int PlayerSAO::getWieldIndex() const
1166 {
1167         return m_wield_index;
1168 }
1169
1170 void PlayerSAO::setWieldIndex(int i)
1171 {
1172         if(i != m_wield_index) {
1173                 m_wield_index = i;
1174         }
1175 }
1176
1177 void PlayerSAO::disconnected()
1178 {
1179         m_peer_id = 0;
1180         m_removed = true;
1181         if(m_player->getPlayerSAO() == this)
1182         {
1183                 m_player->setPlayerSAO(NULL);
1184                 m_player->peer_id = 0;
1185         }
1186 }
1187
1188 std::string PlayerSAO::getPropertyPacket()
1189 {
1190         m_prop.is_visible = (true);
1191         return gob_cmd_set_properties(m_prop);
1192 }
1193
1194 bool PlayerSAO::checkMovementCheat()
1195 {
1196         bool cheated = false;
1197         if(isAttached() || m_is_singleplayer ||
1198                         g_settings->getBool("disable_anticheat"))
1199         {
1200                 m_last_good_position = m_player->getPosition();
1201         }
1202         else
1203         {
1204                 /*
1205                         Check player movements
1206
1207                         NOTE: Actually the server should handle player physics like the
1208                         client does and compare player's position to what is calculated
1209                         on our side. This is required when eg. players fly due to an
1210                         explosion. Altough a node-based alternative might be possible
1211                         too, and much more lightweight.
1212                 */
1213
1214                 float player_max_speed = 0;
1215                 if(m_privs.count("fast") != 0){
1216                         // Fast speed
1217                         player_max_speed = m_player->movement_speed_fast;
1218                 } else {
1219                         // Normal speed
1220                         player_max_speed = m_player->movement_speed_walk;
1221                 }
1222                 // Tolerance. With the lag pool we shouldn't need it.
1223                 //player_max_speed *= 2.5;
1224                 //player_max_speed_up *= 2.5;
1225
1226                 v3f diff = (m_player->getPosition() - m_last_good_position);
1227                 float d_vert = diff.Y;
1228                 diff.Y = 0;
1229                 float d_horiz = diff.getLength();
1230                 float required_time = d_horiz/player_max_speed;
1231                 if(d_vert > 0 && d_vert/player_max_speed > required_time)
1232                         required_time = d_vert/player_max_speed;
1233                 if(m_move_pool.grab(required_time)){
1234                         m_last_good_position = m_player->getPosition();
1235                 } else {
1236                         actionstream<<"Player "<<m_player->getName()
1237                                         <<" moved too fast; resetting position"
1238                                         <<std::endl;
1239                         m_player->setPosition(m_last_good_position);
1240                         cheated = true;
1241                 }
1242         }
1243         return cheated;
1244 }
1245
1246 bool PlayerSAO::getCollisionBox(aabb3f *toset) {
1247         //update collision box
1248         *toset = m_player->getCollisionbox();
1249
1250         toset->MinEdge += m_base_position;
1251         toset->MaxEdge += m_base_position;
1252
1253         return true;
1254 }
1255
1256 bool PlayerSAO::collideWithObjects(){
1257         return true;
1258 }
1259