Move PlayerSAO to dedicated files
[oweals/minetest.git] / src / content_sao.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_sao.h"
21 #include "util/serialize.h"
22 #include "collision.h"
23 #include "environment.h"
24 #include "tool.h" // For ToolCapabilities
25 #include "gamedef.h"
26 #include "nodedef.h"
27 #include "remoteplayer.h"
28 #include "server.h"
29 #include "scripting_server.h"
30 #include "server/player_sao.h"
31 #include "settings.h"
32 #include <algorithm>
33 #include <cmath>
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_pending_removal = 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)
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) const { return false; }
97
98         virtual bool getSelectionBox(aabb3f *toset) const { return false; }
99
100         bool collideWithObjects() const { return false; }
101
102 private:
103         float m_timer1;
104         float m_age;
105 };
106
107 // Prototype (registers item for deserialization)
108 TestSAO proto_TestSAO(NULL, v3f(0,0,0));
109
110 /*
111         LuaEntitySAO
112 */
113
114 // Prototype (registers item for deserialization)
115 LuaEntitySAO proto_LuaEntitySAO(NULL, v3f(0,0,0), "_prototype", "");
116
117 LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
118                 const std::string &name, const std::string &state):
119         UnitSAO(env, pos),
120         m_init_name(name),
121         m_init_state(state)
122 {
123         // Only register type if no environment supplied
124         if(env == NULL){
125                 ServerActiveObject::registerType(getType(), create);
126                 return;
127         }
128 }
129
130 LuaEntitySAO::~LuaEntitySAO()
131 {
132         if(m_registered){
133                 m_env->getScriptIface()->luaentity_Remove(m_id);
134         }
135
136         for (u32 attached_particle_spawner : m_attached_particle_spawners) {
137                 m_env->deleteParticleSpawner(attached_particle_spawner, false);
138         }
139 }
140
141 void LuaEntitySAO::addedToEnvironment(u32 dtime_s)
142 {
143         ServerActiveObject::addedToEnvironment(dtime_s);
144
145         // Create entity from name
146         m_registered = m_env->getScriptIface()->
147                 luaentity_Add(m_id, m_init_name.c_str());
148
149         if(m_registered){
150                 // Get properties
151                 m_env->getScriptIface()->
152                         luaentity_GetProperties(m_id, this, &m_prop);
153                 // Initialize HP from properties
154                 m_hp = m_prop.hp_max;
155                 // Activate entity, supplying serialized state
156                 m_env->getScriptIface()->
157                         luaentity_Activate(m_id, m_init_state, dtime_s);
158         } else {
159                 m_prop.infotext = m_init_name;
160         }
161 }
162
163 ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos,
164                 const std::string &data)
165 {
166         std::string name;
167         std::string state;
168         u16 hp = 1;
169         v3f velocity;
170         v3f rotation;
171
172         while (!data.empty()) { // breakable, run for one iteration
173                 std::istringstream is(data, std::ios::binary);
174                 // 'version' does not allow to incrementally extend the parameter list thus
175                 // we need another variable to build on top of 'version=1'. Ugly hack but works™
176                 u8 version2 = 0;
177                 u8 version = readU8(is);
178
179                 name = deSerializeString(is);
180                 state = deSerializeLongString(is);
181
182                 if (version < 1)
183                         break;
184
185                 hp = readU16(is);
186                 velocity = readV3F1000(is);
187                 // yaw must be yaw to be backwards-compatible
188                 rotation.Y = readF1000(is);
189
190                 if (is.good()) // EOF for old formats
191                         version2 = readU8(is);
192
193                 if (version2 < 1) // PROTOCOL_VERSION < 37
194                         break;
195
196                 // version2 >= 1
197                 rotation.X = readF1000(is);
198                 rotation.Z = readF1000(is);
199
200                 // if (version2 < 2)
201                 //     break;
202                 // <read new values>
203                 break;
204         }
205         // create object
206         infostream << "LuaEntitySAO::create(name=\"" << name << "\" state=\""
207                          << state << "\")" << std::endl;
208         LuaEntitySAO *sao = new LuaEntitySAO(env, pos, name, state);
209         sao->m_hp = hp;
210         sao->m_velocity = velocity;
211         sao->m_rotation = rotation;
212         return sao;
213 }
214
215 void LuaEntitySAO::step(float dtime, bool send_recommended)
216 {
217         if(!m_properties_sent)
218         {
219                 m_properties_sent = true;
220                 std::string str = getPropertyPacket();
221                 // create message and add to list
222                 ActiveObjectMessage aom(getId(), true, str);
223                 m_messages_out.push(aom);
224         }
225
226         // If attached, check that our parent is still there. If it isn't, detach.
227         if(m_attachment_parent_id && !isAttached())
228         {
229                 m_attachment_parent_id = 0;
230                 m_attachment_bone = "";
231                 m_attachment_position = v3f(0,0,0);
232                 m_attachment_rotation = v3f(0,0,0);
233                 sendPosition(false, true);
234         }
235
236         m_last_sent_position_timer += dtime;
237
238         // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally
239         // If the object gets detached this comes into effect automatically from the last known origin
240         if(isAttached())
241         {
242                 v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition();
243                 m_base_position = pos;
244                 m_velocity = v3f(0,0,0);
245                 m_acceleration = v3f(0,0,0);
246         }
247         else
248         {
249                 if(m_prop.physical){
250                         aabb3f box = m_prop.collisionbox;
251                         box.MinEdge *= BS;
252                         box.MaxEdge *= BS;
253                         collisionMoveResult moveresult;
254                         f32 pos_max_d = BS*0.25; // Distance per iteration
255                         v3f p_pos = m_base_position;
256                         v3f p_velocity = m_velocity;
257                         v3f p_acceleration = m_acceleration;
258                         moveresult = collisionMoveSimple(m_env, m_env->getGameDef(),
259                                         pos_max_d, box, m_prop.stepheight, dtime,
260                                         &p_pos, &p_velocity, p_acceleration,
261                                         this, m_prop.collideWithObjects);
262
263                         // Apply results
264                         m_base_position = p_pos;
265                         m_velocity = p_velocity;
266                         m_acceleration = p_acceleration;
267                 } else {
268                         m_base_position += dtime * m_velocity + 0.5 * dtime
269                                         * dtime * m_acceleration;
270                         m_velocity += dtime * m_acceleration;
271                 }
272
273                 if (m_prop.automatic_face_movement_dir &&
274                                 (fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
275                         float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
276                                 + m_prop.automatic_face_movement_dir_offset;
277                         float max_rotation_per_sec =
278                                         m_prop.automatic_face_movement_max_rotation_per_sec;
279
280                         if (max_rotation_per_sec > 0) {
281                                 m_rotation.Y = wrapDegrees_0_360(m_rotation.Y);
282                                 wrappedApproachShortest(m_rotation.Y, target_yaw,
283                                         dtime * max_rotation_per_sec, 360.f);
284                         } else {
285                                 // Negative values of max_rotation_per_sec mean disabled.
286                                 m_rotation.Y = target_yaw;
287                         }
288                 }
289         }
290
291         if(m_registered){
292                 m_env->getScriptIface()->luaentity_Step(m_id, dtime);
293         }
294
295         if (!send_recommended)
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                                 std::fabs(m_rotation.X - m_last_sent_rotation.X) > 1.0f ||
312                                 std::fabs(m_rotation.Y - m_last_sent_rotation.Y) > 1.0f ||
313                                 std::fabs(m_rotation.Z - m_last_sent_rotation.Z) > 1.0f) {
314
315                         sendPosition(true, false);
316                 }
317         }
318
319         if (!m_armor_groups_sent) {
320                 m_armor_groups_sent = true;
321                 // create message and add to list
322                 m_messages_out.emplace(getId(), true, generateUpdateArmorGroupsCommand());
323         }
324
325         if (!m_animation_sent) {
326                 m_animation_sent = true;
327                 std::string str = generateUpdateAnimationCommand();
328                 // create message and add to list
329                 ActiveObjectMessage aom(getId(), true, str);
330                 m_messages_out.push(aom);
331         }
332
333         if (!m_animation_speed_sent) {
334                 m_animation_speed_sent = true;
335                 std::string str = generateUpdateAnimationSpeedCommand();
336                 // create message and add to list
337                 ActiveObjectMessage aom(getId(), true, str);
338                 m_messages_out.push(aom);
339         }
340
341         if (!m_bone_position_sent) {
342                 m_bone_position_sent = true;
343                 for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
344                                 ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
345                         std::string str = generateUpdateBonePositionCommand((*ii).first,
346                                         (*ii).second.X, (*ii).second.Y);
347                         // create message and add to list
348                         ActiveObjectMessage aom(getId(), true, str);
349                         m_messages_out.push(aom);
350                 }
351         }
352
353         if (!m_attachment_sent) {
354                 m_attachment_sent = true;
355                 std::string str = generateUpdateAttachmentCommand();
356                 // create message and add to list
357                 ActiveObjectMessage aom(getId(), true, str);
358                 m_messages_out.push(aom);
359         }
360 }
361
362 std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
363 {
364         std::ostringstream os(std::ios::binary);
365
366         // PROTOCOL_VERSION >= 37
367         writeU8(os, 1); // version
368         os << serializeString(""); // name
369         writeU8(os, 0); // is_player
370         writeU16(os, getId()); //id
371         writeV3F32(os, m_base_position);
372         writeV3F32(os, m_rotation);
373         writeU16(os, m_hp);
374
375         std::ostringstream msg_os(std::ios::binary);
376         msg_os << serializeLongString(getPropertyPacket()); // message 1
377         msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2
378         msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3
379         for (std::unordered_map<std::string, core::vector2d<v3f>>::const_iterator
380                         ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) {
381                 msg_os << serializeLongString(generateUpdateBonePositionCommand((*ii).first,
382                                 (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
383         }
384         msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4
385         int message_count = 4 + m_bone_position.size();
386         for (std::unordered_set<int>::const_iterator ii = m_attachment_child_ids.begin();
387                         (ii != m_attachment_child_ids.end()); ++ii) {
388                 if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
389                         message_count++;
390                         // TODO after a protocol bump: only send the object initialization data
391                         // to older clients (superfluous since this message exists)
392                         msg_os << serializeLongString(obj->generateUpdateInfantCommand(*ii, protocol_version));
393                 }
394         }
395
396         msg_os << serializeLongString(generateSetTextureModCommand());
397         message_count++;
398
399         writeU8(os, message_count);
400         os.write(msg_os.str().c_str(), msg_os.str().size());
401
402         // return result
403         return os.str();
404 }
405
406 void LuaEntitySAO::getStaticData(std::string *result) const
407 {
408         verbosestream<<FUNCTION_NAME<<std::endl;
409         std::ostringstream os(std::ios::binary);
410         // version must be 1 to keep backwards-compatibility. See version2
411         writeU8(os, 1);
412         // name
413         os<<serializeString(m_init_name);
414         // state
415         if(m_registered){
416                 std::string state = m_env->getScriptIface()->
417                         luaentity_GetStaticdata(m_id);
418                 os<<serializeLongString(state);
419         } else {
420                 os<<serializeLongString(m_init_state);
421         }
422         writeU16(os, m_hp);
423         writeV3F1000(os, m_velocity);
424         // yaw
425         writeF1000(os, m_rotation.Y);
426
427         // version2. Increase this variable for new values
428         writeU8(os, 1); // PROTOCOL_VERSION >= 37
429
430         writeF1000(os, m_rotation.X);
431         writeF1000(os, m_rotation.Z);
432
433         // <write new values>
434
435         *result = os.str();
436 }
437
438 u16 LuaEntitySAO::punch(v3f dir,
439                 const ToolCapabilities *toolcap,
440                 ServerActiveObject *puncher,
441                 float time_from_last_punch)
442 {
443         if (!m_registered) {
444                 // Delete unknown LuaEntities when punched
445                 m_pending_removal = true;
446                 return 0;
447         }
448
449         FATAL_ERROR_IF(!puncher, "Punch action called without SAO");
450
451         s32 old_hp = getHP();
452         ItemStack selected_item, hand_item;
453         ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item);
454
455         PunchDamageResult result = getPunchDamage(
456                         m_armor_groups,
457                         toolcap,
458                         &tool_item,
459                         time_from_last_punch);
460
461         bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
462                         time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0);
463
464         if (!damage_handled) {
465                 if (result.did_punch) {
466                         setHP((s32)getHP() - result.damage,
467                                 PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher));
468
469                         // create message and add to list
470                         sendPunchCommand();
471                 }
472         }
473
474         if (getHP() == 0 && !isGone()) {
475                 clearParentAttachment();
476                 clearChildAttachments();
477                 m_env->getScriptIface()->luaentity_on_death(m_id, puncher);
478                 m_pending_removal = true;
479         }
480
481         actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
482                         ", hp=" << puncher->getHP() << ") punched " <<
483                         getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
484                         "), damage=" << (old_hp - (s32)getHP()) <<
485                         (damage_handled ? " (handled by Lua)" : "") << std::endl;
486
487         // TODO: give Lua control over wear
488         return result.wear;
489 }
490
491 void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
492 {
493         if (!m_registered)
494                 return;
495
496         m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
497 }
498
499 void LuaEntitySAO::setPos(const v3f &pos)
500 {
501         if(isAttached())
502                 return;
503         m_base_position = pos;
504         sendPosition(false, true);
505 }
506
507 void LuaEntitySAO::moveTo(v3f pos, bool continuous)
508 {
509         if(isAttached())
510                 return;
511         m_base_position = pos;
512         if(!continuous)
513                 sendPosition(true, true);
514 }
515
516 float LuaEntitySAO::getMinimumSavedMovement()
517 {
518         return 0.1 * BS;
519 }
520
521 std::string LuaEntitySAO::getDescription()
522 {
523         std::ostringstream oss;
524         oss << "LuaEntitySAO \"" << m_init_name << "\" ";
525         auto pos = floatToInt(m_base_position, BS);
526         oss << "at " << PP(pos);
527         return oss.str();
528 }
529
530 void LuaEntitySAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
531 {
532         m_hp = rangelim(hp, 0, U16_MAX);
533 }
534
535 u16 LuaEntitySAO::getHP() const
536 {
537         return m_hp;
538 }
539
540 void LuaEntitySAO::setVelocity(v3f velocity)
541 {
542         m_velocity = velocity;
543 }
544
545 v3f LuaEntitySAO::getVelocity()
546 {
547         return m_velocity;
548 }
549
550 void LuaEntitySAO::setAcceleration(v3f acceleration)
551 {
552         m_acceleration = acceleration;
553 }
554
555 v3f LuaEntitySAO::getAcceleration()
556 {
557         return m_acceleration;
558 }
559
560 void LuaEntitySAO::setTextureMod(const std::string &mod)
561 {
562         m_current_texture_modifier = mod;
563         // create message and add to list
564         m_messages_out.emplace(getId(), true, generateSetTextureModCommand());
565 }
566
567 std::string LuaEntitySAO::getTextureMod() const
568 {
569         return m_current_texture_modifier;
570 }
571
572
573 std::string LuaEntitySAO::generateSetTextureModCommand() const
574 {
575         std::ostringstream os(std::ios::binary);
576         // command
577         writeU8(os, AO_CMD_SET_TEXTURE_MOD);
578         // parameters
579         os << serializeString(m_current_texture_modifier);
580         return os.str();
581 }
582
583 std::string LuaEntitySAO::generateSetSpriteCommand(v2s16 p, u16 num_frames,
584         f32 framelength, bool select_horiz_by_yawpitch)
585 {
586         std::ostringstream os(std::ios::binary);
587         // command
588         writeU8(os, AO_CMD_SET_SPRITE);
589         // parameters
590         writeV2S16(os, p);
591         writeU16(os, num_frames);
592         writeF32(os, framelength);
593         writeU8(os, select_horiz_by_yawpitch);
594         return os.str();
595 }
596
597 void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
598                 bool select_horiz_by_yawpitch)
599 {
600         std::string str = generateSetSpriteCommand(
601                 p,
602                 num_frames,
603                 framelength,
604                 select_horiz_by_yawpitch
605         );
606         // create message and add to list
607         m_messages_out.emplace(getId(), true, str);
608 }
609
610 std::string LuaEntitySAO::getName()
611 {
612         return m_init_name;
613 }
614
615 std::string LuaEntitySAO::getPropertyPacket()
616 {
617         return generateSetPropertiesCommand(m_prop);
618 }
619
620 void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
621 {
622         // If the object is attached client-side, don't waste bandwidth sending its position to clients
623         if(isAttached())
624                 return;
625
626         m_last_sent_move_precision = m_base_position.getDistanceFrom(
627                         m_last_sent_position);
628         m_last_sent_position_timer = 0;
629         m_last_sent_position = m_base_position;
630         m_last_sent_velocity = m_velocity;
631         //m_last_sent_acceleration = m_acceleration;
632         m_last_sent_rotation = m_rotation;
633
634         float update_interval = m_env->getSendRecommendedInterval();
635
636         std::string str = generateUpdatePositionCommand(
637                 m_base_position,
638                 m_velocity,
639                 m_acceleration,
640                 m_rotation,
641                 do_interpolate,
642                 is_movement_end,
643                 update_interval
644         );
645         // create message and add to list
646         m_messages_out.emplace(getId(), false, str);
647 }
648
649 bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
650 {
651         if (m_prop.physical)
652         {
653                 //update collision box
654                 toset->MinEdge = m_prop.collisionbox.MinEdge * BS;
655                 toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS;
656
657                 toset->MinEdge += m_base_position;
658                 toset->MaxEdge += m_base_position;
659
660                 return true;
661         }
662
663         return false;
664 }
665
666 bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
667 {
668         if (!m_prop.is_visible || !m_prop.pointable) {
669                 return false;
670         }
671
672         toset->MinEdge = m_prop.selectionbox.MinEdge * BS;
673         toset->MaxEdge = m_prop.selectionbox.MaxEdge * BS;
674
675         return true;
676 }
677
678 bool LuaEntitySAO::collideWithObjects() const
679 {
680         return m_prop.collideWithObjects;
681 }