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