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