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