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