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