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