diff --git a/data/heart.png b/data/heart.png new file mode 100644 index 000000000000..ddd273dd2f09 Binary files /dev/null and b/data/heart.png differ diff --git a/data/oerkki1.png b/data/oerkki1.png new file mode 100644 index 000000000000..c32fb99dbc93 Binary files /dev/null and b/data/oerkki1.png differ diff --git a/data/stick.png b/data/stick.png index 7a4663cc3a3e..2d31797f0067 100644 Binary files a/data/stick.png and b/data/stick.png differ diff --git a/data/tool_mesepick.png b/data/tool_mesepick.png index 886f4b21c9cd..a1f3812e0992 100644 Binary files a/data/tool_mesepick.png and b/data/tool_mesepick.png differ diff --git a/data/tool_steelaxe.png b/data/tool_steelaxe.png index 85267ae7faec..390dbb0870e7 100644 Binary files a/data/tool_steelaxe.png and b/data/tool_steelaxe.png differ diff --git a/data/tool_steelpick.png b/data/tool_steelpick.png index 4bb5f8be47c3..7982dafebf65 100644 Binary files a/data/tool_steelpick.png and b/data/tool_steelpick.png differ diff --git a/data/tool_steelshovel.png b/data/tool_steelshovel.png index 61d90b12aec3..ed8413846005 100644 Binary files a/data/tool_steelshovel.png and b/data/tool_steelshovel.png differ diff --git a/data/tool_stoneaxe.png b/data/tool_stoneaxe.png index bcb5896893e0..0c5414af5703 100644 Binary files a/data/tool_stoneaxe.png and b/data/tool_stoneaxe.png differ diff --git a/data/tool_stonepick.png b/data/tool_stonepick.png index 9ca3a5e03524..b34de6f327a6 100644 Binary files a/data/tool_stonepick.png and b/data/tool_stonepick.png differ diff --git a/data/tool_stoneshovel.png b/data/tool_stoneshovel.png index 53eb72307e20..ba5243101a11 100644 Binary files a/data/tool_stoneshovel.png and b/data/tool_stoneshovel.png differ diff --git a/data/tool_woodaxe.png b/data/tool_woodaxe.png index cb0d95f28ffd..34f54eff966a 100644 Binary files a/data/tool_woodaxe.png and b/data/tool_woodaxe.png differ diff --git a/data/tool_woodpick.png b/data/tool_woodpick.png index 3592495154f6..ea728cca3fd5 100644 Binary files a/data/tool_woodpick.png and b/data/tool_woodpick.png differ diff --git a/data/tool_woodshovel.png b/data/tool_woodshovel.png index 2645952d00b2..649ab4c38a79 100644 Binary files a/data/tool_woodshovel.png and b/data/tool_woodshovel.png differ diff --git a/minetest.conf.example b/minetest.conf.example index c78266e7bb03..02cfb7aada1b 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -52,6 +52,8 @@ # Set to true to enable creative mode (unlimited inventory) #creative_mode = false +#enable_damage = false + # Player and object positions are sent at intervals specified by this #objectdata_inverval = 0.2 diff --git a/src/activeobject.h b/src/activeobject.h index e1fc6beaf1ee..382f7f7981d3 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -40,6 +40,7 @@ struct ActiveObjectMessage #define ACTIVEOBJECT_TYPE_TEST 1 #define ACTIVEOBJECT_TYPE_ITEM 2 #define ACTIVEOBJECT_TYPE_RAT 3 +#define ACTIVEOBJECT_TYPE_OERKKI1 4 /* Parent class for ServerActiveObject and ClientActiveObject diff --git a/src/client.cpp b/src/client.cpp index e2877f5fc634..702247f66364 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -90,6 +90,7 @@ Client::Client( m_connection_reinit_timer = 0.0; m_avg_rtt_timer = 0.0; m_playerpos_send_timer = 0.0; + m_ignore_damage_timer = 0.0; //m_env_mutex.Init(); //m_con_mutex.Init(); @@ -154,6 +155,10 @@ void Client::step(float dtime) if(dtime > 2.0) dtime = 2.0; + if(m_ignore_damage_timer > dtime) + m_ignore_damage_timer -= dtime; + else + m_ignore_damage_timer = 0.0; //dstream<<"Client steps "< data(data_maxsize); u16 sender_peer_id; u32 datasize; @@ -1294,219 +1337,56 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } } } - else - { - dout_client<hp = hp; } - - if(datasize < 2) - return true; - - ToClientCommand command = (ToClientCommand)readU16(&data[0]); - - if(command == TOCLIENT_BLOCKDATA) + else if(command == TOCLIENT_MOVE_PLAYER) { - // Ignore too small packet - if(datasize < 8) - return true; - - v3s16 p; - p.X = readS16(&data[2]); - p.Y = readS16(&data[4]); - p.Z = readS16(&data[6]); - - /*dout_client<getPos(); - if(sp != p2d) - { - dstream<<"ERROR: Got sector with getPos()=" - <<"("<getPos() == p2d); - - //TimeTaker timer("MapBlock deSerialize"); - // 0ms - - try{ - block = sector->getBlockNoCreate(p.Y); - /* - Update an existing block - */ - //dstream<<"Updating"<deSerialize(istr, ser_version); - //block->setChangedFlag(); - } - catch(InvalidPositionException &e) - { - /* - Create a new block - */ - //dstream<<"Creating new"<deSerialize(istr, ser_version); - sector->insertBlock(block); - //block->setChangedFlag(); - - //DEBUG - /*NodeMod mod; - mod.type = NODEMOD_CHANGECONTENT; - mod.param = CONTENT_MESE; - block->setTempMod(v3s16(8,10,8), mod); - block->setTempMod(v3s16(8,9,8), mod); - block->setTempMod(v3s16(8,8,8), mod); - block->setTempMod(v3s16(8,7,8), mod); - block->setTempMod(v3s16(8,6,8), mod);*/ -#if 0 - /* - Add some coulds - Well, this is a dumb way to do it, they should just - be drawn as separate objects. But the looks of them - can be tested this way. - */ - if(p.Y == 3) - { - NodeMod mod; - mod.type = NODEMOD_CHANGECONTENT; - mod.param = CONTENT_CLOUD; - v3s16 p2; - p2.Y = 8; - for(p2.X=3; p2.X<=13; p2.X++) - for(p2.Z=3; p2.Z<=13; p2.Z++) - { - block->setTempMod(p2, mod); - } - } -#endif - } - } //envlock - - /* - Acknowledge block. - */ - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - u32 replysize = 2+1+6; - SharedBuffer reply(replysize); - writeU16(&reply[0], TOSERVER_GOTBLOCKS); - reply[2] = 1; - writeV3S16(&reply[3], p); - // Send as reliable - m_con.Send(PEER_ID_SERVER, 1, reply, true); + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + v3f pos = readV3F1000(is); + f32 pitch = readF1000(is); + f32 yaw = readF1000(is); + player->setPosition(pos); + /*player->setPitch(pitch); + player->setYaw(yaw);*/ + + dstream<<"Client got TOCLIENT_MOVE_PLAYER" + <<" pos=("<replaceMesh(mesh_new); - } + ClientEvent event; + event.type = CE_PLAYER_FORCE_MOVE; + event.player_force_move.pitch = pitch; + event.player_force_move.yaw = yaw; + m_client_event_queue.push_back(event); + + // Ignore damage for a few seconds, so that the player doesn't + // get damage from falling on ground + m_ignore_damage_timer = 3.0; } else { dout_client< data, bool reliable) { @@ -1514,28 +1394,6 @@ void Client::Send(u16 channelnum, SharedBuffer data, bool reliable) m_con.Send(PEER_ID_SERVER, channelnum, data, reliable); } -#if 0 -IncomingPacket Client::getPacket() -{ - JMutexAutoLock lock(m_incoming_queue_mutex); - - core::list::Iterator i; - // Refer to first one - i = m_incoming_queue.begin(); - - // If queue is empty, return empty packet - if(i == m_incoming_queue.end()){ - IncomingPacket packet; - return packet; - } - - // Pop out first packet and return it - IncomingPacket packet = *i; - m_incoming_queue.erase(i); - return packet; -} -#endif - void Client::groundAction(u8 action, v3s16 nodepos_undersurface, v3s16 nodepos_oversurface, u16 item) { @@ -1739,6 +1597,21 @@ void Client::sendChatMessage(const std::wstring &message) Send(0, data, true); } +void Client::sendDamage(u8 damage) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOSERVER_DAMAGE); + writeU8(os, damage); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::sendPlayerPos() { //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out @@ -2061,6 +1934,13 @@ u32 Client::getDayNightRatio() return m_env.getDayNightRatio(); } +u16 Client::getHP() +{ + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + return player->hp; +} + void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) { /*dstream<<"Client::addUpdateMeshTask(): " @@ -2141,3 +2021,15 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server) catch(InvalidPositionException &e){} } +ClientEvent Client::getClientEvent() +{ + if(m_client_event_queue.size() == 0) + { + ClientEvent event; + event.type = CE_NONE; + return event; + } + return m_client_event_queue.pop_front(); +} + + diff --git a/src/client.h b/src/client.h index ef3dd435a04b..ee73cc42ce83 100644 --- a/src/client.h +++ b/src/client.h @@ -174,55 +174,28 @@ class MeshUpdateThread : public SimpleThread MutexedQueue m_queue_out; }; -#if 0 -struct IncomingPacket +enum ClientEventType { - IncomingPacket() - { - m_data = NULL; - m_datalen = 0; - m_refcount = NULL; - } - IncomingPacket(const IncomingPacket &a) - { - m_data = a.m_data; - m_datalen = a.m_datalen; - m_refcount = a.m_refcount; - if(m_refcount != NULL) - (*m_refcount)++; - } - IncomingPacket(u8 *data, u32 datalen) - { - m_data = new u8[datalen]; - memcpy(m_data, data, datalen); - m_datalen = datalen; - m_refcount = new s32(1); - } - ~IncomingPacket() - { - if(m_refcount != NULL){ - assert(*m_refcount > 0); - (*m_refcount)--; - if(*m_refcount == 0){ - if(m_data != NULL) - delete[] m_data; - delete m_refcount; - } - } - } - /*IncomingPacket & operator=(IncomingPacket a) - { - m_data = a.m_data; - m_datalen = a.m_datalen; - m_refcount = a.m_refcount; - (*m_refcount)++; - return *this; - }*/ - u8 *m_data; - u32 m_datalen; - s32 *m_refcount; + CE_NONE, + CE_PLAYER_DAMAGE, + CE_PLAYER_FORCE_MOVE +}; + +struct ClientEvent +{ + ClientEventType type; + union{ + struct{ + } none; + struct{ + u8 amount; + } player_damage; + struct{ + f32 pitch; + f32 yaw; + } player_force_move; + }; }; -#endif class Client : public con::PeerHandler, public InventoryManager { @@ -281,6 +254,7 @@ class Client : public con::PeerHandler, public InventoryManager void sendSignNodeText(v3s16 p, std::string text); void sendInventoryAction(InventoryAction *a); void sendChatMessage(const std::wstring &message); + void sendDamage(u8 damage); // locks envlock void removeNode(v3s16 p); @@ -330,6 +304,8 @@ class Client : public con::PeerHandler, public InventoryManager u32 getDayNightRatio(); + u16 getHP(); + //void updateSomeExpiredMeshes(); void setTempMod(v3s16 p, NodeMod mod) @@ -394,13 +370,13 @@ class Client : public con::PeerHandler, public InventoryManager u64 getMapSeed(){ return m_map_seed; } - /* - These are not thread-safe - */ void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false); // Including blocks at appropriate edges void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false); + // Get event from queue. CE_NONE is returned if queue is empty. + ClientEvent getClientEvent(); + private: // Virtual methods from con::PeerHandler @@ -419,6 +395,7 @@ class Client : public con::PeerHandler, public InventoryManager float m_connection_reinit_timer; float m_avg_rtt_timer; float m_playerpos_send_timer; + float m_ignore_damage_timer; // Used after server moves player MeshUpdateThread m_mesh_update_thread; @@ -454,6 +431,8 @@ class Client : public con::PeerHandler, public InventoryManager u64 m_map_seed; InventoryContext m_inventory_context; + + Queue m_client_event_queue; }; #endif // !SERVER diff --git a/src/clientobject.cpp b/src/clientobject.cpp index 5b744de6ce0d..78258add88bc 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -494,7 +494,7 @@ void RatCAO::updateLight(u8 light_at_pos) v3s16 RatCAO::getLightPosition() { - return floatToInt(m_position, BS); + return floatToInt(m_position+v3f(0,BS*0.5,0), BS); } void RatCAO::updateNodePos() @@ -552,4 +552,181 @@ void RatCAO::initialize(const std::string &data) updateNodePos(); } +/* + Oerkki1CAO +*/ + +#include "inventory.h" + +// Prototype +Oerkki1CAO proto_Oerkki1CAO; + +Oerkki1CAO::Oerkki1CAO(): + ClientActiveObject(0), + m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2.,BS/3.), + m_node(NULL), + m_position(v3f(0,10*BS,0)), + m_yaw(0) +{ + ClientActiveObject::registerType(getType(), create); +} + +Oerkki1CAO::~Oerkki1CAO() +{ +} + +ClientActiveObject* Oerkki1CAO::create() +{ + return new Oerkki1CAO(); +} + +void Oerkki1CAO::addToScene(scene::ISceneManager *smgr) +{ + if(m_node != NULL) + return; + + video::IVideoDriver* driver = smgr->getVideoDriver(); + + scene::SMesh *mesh = new scene::SMesh(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::SColor c(255,255,255,255); + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,0,0, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,0,0, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,BS*2,0, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,BS*2,0, 0,0,0, c, 0,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); + //buf->getMaterial().setTexture(0, NULL); + buf->getMaterial().setTexture + (0, driver->getTexture(porting::getDataPath("oerkki1.png").c_str())); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); + m_node = smgr->addMeshSceneNode(mesh, NULL); + mesh->drop(); + // Set it to use the materials of the meshbuffers directly. + // This is needed for changing the texture in the future + m_node->setReadOnlyMaterials(true); + updateNodePos(); +} + +void Oerkki1CAO::removeFromScene() +{ + if(m_node == NULL) + return; + + m_node->remove(); + m_node = NULL; +} + +void Oerkki1CAO::updateLight(u8 light_at_pos) +{ + if(m_node == NULL) + return; + + u8 li = decode_light(light_at_pos); + video::SColor color(255,li,li,li); + + scene::IMesh *mesh = m_node->getMesh(); + if(mesh == NULL) + return; + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; isetPosition(m_position); + m_node->setPosition(pos_translator.vect_show); + + v3f rot = m_node->getRotation(); + rot.Y = 180.0 - m_yaw + 90.0; + m_node->setRotation(rot); +} + +void Oerkki1CAO::step(float dtime, ClientEnvironment *env) +{ + pos_translator.translate(dtime); + updateNodePos(); + + LocalPlayer *player = env->getLocalPlayer(); + assert(player); + + v3f playerpos = player->getPosition(); + v2f playerpos_2d(playerpos.X,playerpos.Z); + v2f objectpos_2d(m_position.X,m_position.Z); + + if(fabs(objectpos_2d.Y - playerpos_2d.Y) < 2.0*BS && + objectpos_2d.getDistanceFrom(playerpos_2d) < 1.0*BS) + { + if(m_attack_interval.step(dtime, 0.5)) + { + env->damageLocalPlayer(2); + } + } +} + +void Oerkki1CAO::processMessage(const std::string &data) +{ + //dstream<<"Oerkki1CAO: Got message"<* getSelectionBox() + {return &m_selection_box;} + v3f getPosition() + {return pos_translator.vect_show;} + //{return m_position;} + +private: + IntervalLimiter m_attack_interval; + core::aabbox3d m_selection_box; + scene::IMeshSceneNode *m_node; + v3f m_position; + float m_yaw; + SmoothTranslator pos_translator; +}; + #endif diff --git a/src/clientserver.h b/src/clientserver.h index fadafed5fe4d..46ffa5eab365 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -33,15 +33,18 @@ enum ToClientCommand [0] u16 TOSERVER_INIT [2] u8 deployed version - [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd - [4] u64 map seed (new as of 2011-02-27) + [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd + ([4] u64 map seed (new as of 2011-02-27)) + + NOTE: The position in here is deprecated; position is + explicitly sent afterwards */ TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks TOCLIENT_ADDNODE = 0x21, TOCLIENT_REMOVENODE = 0x22, - TOCLIENT_PLAYERPOS = 0x23, + TOCLIENT_PLAYERPOS = 0x23, // Obsolete /* [0] u16 command // Followed by an arbitary number of these: @@ -62,9 +65,9 @@ enum ToClientCommand [N] char[20] name */ - TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Not used + TOCLIENT_OPT_BLOCK_NOT_FOUND = 0x25, // Obsolete - TOCLIENT_SECTORMETA = 0x26, // Not used + TOCLIENT_SECTORMETA = 0x26, // Obsolete /* [0] u16 command [2] u8 sector count @@ -134,6 +137,19 @@ enum ToClientCommand } */ + TOCLIENT_HP = 0x33, + /* + u16 command + u8 hp + */ + + TOCLIENT_MOVE_PLAYER = 0x34, + /* + u16 command + v3f1000 player position + f1000 player pitch + f1000 player yaw + */ }; enum ToServerCommand @@ -155,9 +171,9 @@ enum ToServerCommand [0] u16 TOSERVER_INIT2 */ - TOSERVER_GETBLOCK=0x20, // Not used - TOSERVER_ADDNODE = 0x21, // Not used - TOSERVER_REMOVENODE = 0x22, // deprecated + TOSERVER_GETBLOCK=0x20, // Obsolete + TOSERVER_ADDNODE = 0x21, // Obsolete + TOSERVER_REMOVENODE = 0x22, // Obsolete TOSERVER_PLAYERPOS = 0x23, /* @@ -186,7 +202,7 @@ enum ToServerCommand ... */ - TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // deprecated + TOSERVER_ADDNODE_FROM_INVENTORY = 0x26, // Obsolete /* [0] u16 command [2] v3s16 pos @@ -218,9 +234,9 @@ enum ToServerCommand 3: digging completed */ - TOSERVER_RELEASE = 0x29, // Not used + TOSERVER_RELEASE = 0x29, // Obsolete - TOSERVER_SIGNTEXT = 0x30, + TOSERVER_SIGNTEXT = 0x30, // Old signs /* u16 command v3s16 blockpos @@ -257,7 +273,12 @@ enum ToServerCommand [3] u16 id [5] u16 item */ - + + TOSERVER_DAMAGE = 0x35, + /* + u16 command + u8 amount + */ }; inline SharedBuffer makePacket_TOCLIENT_TIME_OF_DAY(u16 time) diff --git a/src/collision.cpp b/src/collision.cpp index 83cefe4d1e1a..63186a84a5e7 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -70,6 +70,7 @@ collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, /* Go through every node around the object + TODO: Calculate the range of nodes that need to be checked */ for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++) for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++) diff --git a/src/collision.h b/src/collision.h index 17243140148a..9c913c6a9720 100644 --- a/src/collision.h +++ b/src/collision.h @@ -38,6 +38,16 @@ collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, f32 dtime, v3f &pos_f, v3f &speed_f); //{return collisionMoveResult();} +enum CollisionType +{ + COLLISION_FALL +}; + +struct CollisionInfo +{ + CollisionType t; + f32 speed; +}; #endif diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 2a548d406e3a..b5d86391414b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -55,6 +55,7 @@ void set_default_settings() g_settings.setDefault("enable_experimental", "false"); g_settings.setDefault("creative_mode", "false"); + g_settings.setDefault("enable_damage", "false"); //TODO: Set to true g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("active_object_range", "2"); diff --git a/src/environment.cpp b/src/environment.cpp index 3f95ed9f9be7..b3055ca6f7b8 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "filesys.h" #include "porting.h" +#include "collision.h" Environment::Environment() { @@ -377,6 +378,55 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir) } } +#if 0 +void spawnRandomObjects(MapBlock *block) +{ + for(s16 z0=0; z0getNodeNoEx(p); + if(n.d == CONTENT_IGNORE) + continue; + if(content_features(n.d).liquid_type != LIQUID_NONE) + continue; + if(content_features(n.d).walkable) + { + last_node_walkable = true; + continue; + } + if(last_node_walkable) + { + // If block contains light information + if(content_features(n.d).param_type == CPT_LIGHT) + { + if(n.getLight(LIGHTBANK_DAY) <= 5) + { + if(myrand() % 1000 == 0) + { + v3f pos_f = intToFloat(p+block->getPosRelative(), BS); + pos_f.Y -= BS*0.4; + ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f); + std::string data = obj->getStaticData(); + StaticObject s_obj(obj->getType(), + obj->getBasePosition(), data); + // Add one + block->m_static_objects.insert(0, s_obj); + delete obj; + block->setChangedFlag(); + } + } + } + } + last_node_walkable = false; + } + } +} +#endif + void ServerEnvironment::step(float dtime) { DSTACK(__FUNCTION_NAME); @@ -429,26 +479,29 @@ void ServerEnvironment::step(float dtime) } } } - + /* Step active objects */ - - bool send_recommended = false; - m_send_recommended_timer += dtime; - if(m_send_recommended_timer > 0.15) { - m_send_recommended_timer = 0; - send_recommended = true; - } + //TimeTaker timer("Step active objects"); - for(core::map::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) - { - ServerActiveObject* obj = i.getNode()->getValue(); - // Step object, putting messages directly to the queue - obj->step(dtime, m_active_object_messages, send_recommended); + bool send_recommended = false; + m_send_recommended_timer += dtime; + if(m_send_recommended_timer > 0.15) + { + m_send_recommended_timer = 0; + send_recommended = true; + } + + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ServerActiveObject* obj = i.getNode()->getValue(); + // Step object, putting messages directly to the queue + obj->step(dtime, m_active_object_messages, send_recommended); + } } if(m_object_management_interval.step(dtime, 0.5)) @@ -506,7 +559,7 @@ void ServerEnvironment::step(float dtime) const s16 to_active_max_blocks = 3; - const f32 to_static_max_f = (to_active_max_blocks+1)*MAP_BLOCKSIZE*BS; + const f32 to_static_max_f = (to_active_max_blocks+2)*MAP_BLOCKSIZE*BS; /* Convert stored objects from blocks near the players to active. @@ -719,7 +772,8 @@ void ServerEnvironment::step(float dtime) //TestSAO *obj = new TestSAO(this, 0, pos); //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1"); - ServerActiveObject *obj = new RatSAO(this, 0, pos); + //ServerActiveObject *obj = new RatSAO(this, 0, pos); + ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos); addActiveObject(obj); } #endif @@ -976,14 +1030,18 @@ void ClientEnvironment::step(float dtime) //TimeTaker timer("Client m_map->timerUpdate()", g_device); m_map->timerUpdate(dtime); } - + + // Get local player + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + // collision info queue + core::list player_collisions; + /* Get the speed the player is going */ f32 player_speed = 0.001; // just some small value - LocalPlayer *lplayer = getLocalPlayer(); - if(lplayer) - player_speed = lplayer->getSpeed().getLength(); + player_speed = lplayer->getSpeed().getLength(); /* Maximum position increment @@ -1036,20 +1094,18 @@ void ClientEnvironment::step(float dtime) */ { - Player *player = getLocalPlayer(); - - v3f playerpos = player->getPosition(); + v3f lplayerpos = lplayer->getPosition(); // Apply physics if(free_move == false) { // Gravity - v3f speed = player->getSpeed(); - if(player->swimming_up == false) + v3f speed = lplayer->getSpeed(); + if(lplayer->swimming_up == false) speed.Y -= 9.81 * BS * dtime_part * 2; // Water resistance - if(player->in_water_stable || player->in_water) + if(lplayer->in_water_stable || lplayer->in_water) { f32 max_down = 2.0*BS; if(speed.Y < -max_down) speed.Y = -max_down; @@ -1061,19 +1117,47 @@ void ClientEnvironment::step(float dtime) } } - player->setSpeed(speed); + lplayer->setSpeed(speed); } /* - Move the player. + Move the lplayer. This also does collision detection. */ - player->move(dtime_part, *m_map, position_max_increment); + lplayer->move(dtime_part, *m_map, position_max_increment, + &player_collisions); } } while(dtime_downcount > 0.001); //std::cout<<"Looped "<::Iterator + i = player_collisions.begin(); + i != player_collisions.end(); i++) + { + CollisionInfo &info = *i; + if(info.t == COLLISION_FALL) + { + //f32 tolerance = BS*10; // 2 without damage + f32 tolerance = BS*12; // 3 without damage + f32 factor = 1; + if(info.speed > tolerance) + { + f32 damage_f = (info.speed - tolerance)/BS*factor; + u16 damage = (u16)(damage_f+0.5); + if(lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + m_client_event_queue.push_back(event); + } + } + } /* Stuff that can be done in an arbitarily large dtime @@ -1287,6 +1371,30 @@ void ClientEnvironment::processActiveObjectMessage(u16 id, obj->processMessage(data); } +/* + Callbacks for activeobjects +*/ + +void ClientEnvironment::damageLocalPlayer(u8 damage) +{ + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + + if(lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + m_client_event_queue.push_back(event); +} + +/* + Client likes to call these +*/ + void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, core::array &dest) { @@ -1307,6 +1415,16 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, } } +ClientEnvEvent ClientEnvironment::getClientEvent() +{ + if(m_client_event_queue.size() == 0) + { + ClientEnvEvent event; + event.type = CEE_NONE; + return event; + } + return m_client_event_queue.pop_front(); +} #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index e82cea6ae275..00192d26278d 100644 --- a/src/environment.h +++ b/src/environment.h @@ -170,6 +170,24 @@ class ServerEnvironment : public Environment Client uses an environment mutex. */ +enum ClientEnvEventType +{ + CEE_NONE, + CEE_PLAYER_DAMAGE +}; + +struct ClientEnvEvent +{ + ClientEnvEventType type; + union { + struct{ + } none; + struct{ + u8 amount; + } player_damage; + }; +}; + class ClientEnvironment : public Environment { public: @@ -214,15 +232,29 @@ class ClientEnvironment : public Environment void removeActiveObject(u16 id); void processActiveObjectMessage(u16 id, const std::string &data); + + /* + Callbacks for activeobjects + */ + + void damageLocalPlayer(u8 damage); + + /* + Client likes to call these + */ // Get all nearby objects void getActiveObjects(v3f origin, f32 max_d, core::array &dest); + // Get event from queue. CEE_NONE is returned if queue is empty. + ClientEnvEvent getClientEvent(); + private: ClientMap *m_map; scene::ISceneManager *m_smgr; core::map m_active_objects; + Queue m_client_event_queue; }; #endif diff --git a/src/inventory.h b/src/inventory.h index d2d23542ea3c..f162952d32f1 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -369,6 +369,12 @@ class ToolItem : public InventoryItem basename = "tool_stoneaxe.png"; else if(m_toolname == "SteelAxe") basename = "tool_steelaxe.png"; + else if(m_toolname == "WSword") + basename = "tool_woodsword.png"; + else if(m_toolname == "STSword") + basename = "tool_stonesword.png"; + else if(m_toolname == "SteelSword") + basename = "tool_steelsword.png"; else basename = "cloud.png"; diff --git a/src/main.cpp b/src/main.cpp index bf5f3182b36c..436e5babc955 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -93,6 +93,10 @@ SUGG: Meshes of blocks could be split into 6 meshes facing into SUGG: Calculate lighting per vertex to get a lighting effect like in bartwe's game +SUGG: Background music based on cellular automata? + http://www.earslap.com/projectslab/otomata + + Gaming ideas: ------------- @@ -126,6 +130,12 @@ Game content: - You can drop on top of it, and have some time to attack there before he shakes you off +- Maybe the difficulty could come from monsters getting tougher in + far-away places, and the player starting to need something from + there when time goes by. + - The player would have some of that stuff at the beginning, and + would need new supplies of it when it runs out + Documentation: -------------- @@ -1210,7 +1220,7 @@ void updateViewingRange(f32 frametime_in, Client *client) void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, v2s32 centerlowerpos, s32 imgsize, s32 itemcount, - Inventory *inventory) + Inventory *inventory, s32 halfheartcount) { InventoryList *mainlist = inventory->getList("main"); if(mainlist == NULL) @@ -1259,6 +1269,40 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, drawInventoryItem(driver, font, item, rect, NULL); } } + + /* + Draw hearts + */ + { + video::ITexture *heart_texture = + driver->getTexture(porting::getDataPath("heart.png").c_str()); + v2s32 p = pos + v2s32(0, -20); + for(s32 i=0; i rect(0,0,16,16); + rect += p; + driver->draw2DImage(heart_texture, rect, + core::rect(core::position2d(0,0), + core::dimension2di(heart_texture->getOriginalSize())), + NULL, colors, true); + p += v2s32(20,0); + } + if(halfheartcount % 2 == 1) + { + const video::SColor color(255,255,255,255); + const video::SColor colors[] = {color,color,color,color}; + core::rect rect(0,0,16/2,16); + rect += p; + core::dimension2di srcd(heart_texture->getOriginalSize()); + srcd.Width /= 2; + driver->draw2DImage(heart_texture, rect, + core::rect(core::position2d(0,0), srcd), + NULL, colors, true); + p += v2s32(20,0); + } + } } #if 0 @@ -1519,6 +1563,215 @@ void SpeedTests() } } +void getPointedNode(v3f player_position, + v3f camera_direction, v3f camera_position, + bool &nodefound, core::line3d shootline, + v3s16 &nodepos, v3s16 &neighbourpos, + core::aabbox3d &nodehilightbox, + f32 d) +{ + assert(g_client); + + f32 mindistance = BS * 1001; + + v3s16 pos_i = floatToInt(player_position, BS); + + /*std::cout<<"pos_i=("<0 ? a : 1); + s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1); + s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1); + + for(s16 y = ystart; y <= yend; y++) + for(s16 z = zstart; z <= zend; z++) + for(s16 x = xstart; x <= xend; x++) + { + MapNode n; + try + { + n = g_client->getNode(v3s16(x,y,z)); + if(content_pointable(n.d) == false) + continue; + } + catch(InvalidPositionException &e) + { + continue; + } + + v3s16 np(x,y,z); + v3f npf = intToFloat(np, BS); + + f32 d = 0.01; + + v3s16 dirs[6] = { + v3s16(0,0,1), // back + v3s16(0,1,0), // top + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,-1,0), // bottom + v3s16(-1,0,0), // left + }; + + /* + Meta-objects + */ + if(n.d == CONTENT_TORCH) + { + v3s16 dir = unpackDir(n.dir); + v3f dir_f = v3f(dir.X, dir.Y, dir.Z); + dir_f *= BS/2 - BS/6 - BS/20; + v3f cpf = npf + dir_f; + f32 distance = (cpf - camera_position).getLength(); + + core::aabbox3d box; + + // bottom + if(dir == v3s16(0,-1,0)) + { + box = core::aabbox3d( + npf - v3f(BS/6, BS/2, BS/6), + npf + v3f(BS/6, -BS/2+BS/3*2, BS/6) + ); + } + // top + else if(dir == v3s16(0,1,0)) + { + box = core::aabbox3d( + npf - v3f(BS/6, -BS/2+BS/3*2, BS/6), + npf + v3f(BS/6, BS/2, BS/6) + ); + } + // side + else + { + box = core::aabbox3d( + cpf - v3f(BS/6, BS/3, BS/6), + cpf + v3f(BS/6, BS/3, BS/6) + ); + } + + if(distance < mindistance) + { + if(box.intersectsWithLine(shootline)) + { + nodefound = true; + nodepos = np; + neighbourpos = np; + mindistance = distance; + nodehilightbox = box; + } + } + } + else if(n.d == CONTENT_SIGN_WALL) + { + v3s16 dir = unpackDir(n.dir); + v3f dir_f = v3f(dir.X, dir.Y, dir.Z); + dir_f *= BS/2 - BS/6 - BS/20; + v3f cpf = npf + dir_f; + f32 distance = (cpf - camera_position).getLength(); + + v3f vertices[4] = + { + v3f(BS*0.42,-BS*0.35,-BS*0.4), + v3f(BS*0.49, BS*0.35, BS*0.4), + }; + + for(s32 i=0; i<2; i++) + { + if(dir == v3s16(1,0,0)) + vertices[i].rotateXZBy(0); + if(dir == v3s16(-1,0,0)) + vertices[i].rotateXZBy(180); + if(dir == v3s16(0,0,1)) + vertices[i].rotateXZBy(90); + if(dir == v3s16(0,0,-1)) + vertices[i].rotateXZBy(-90); + if(dir == v3s16(0,-1,0)) + vertices[i].rotateXYBy(-90); + if(dir == v3s16(0,1,0)) + vertices[i].rotateXYBy(90); + + vertices[i] += npf; + } + + core::aabbox3d box; + + box = core::aabbox3d(vertices[0]); + box.addInternalPoint(vertices[1]); + + if(distance < mindistance) + { + if(box.intersectsWithLine(shootline)) + { + nodefound = true; + nodepos = np; + neighbourpos = np; + mindistance = distance; + nodehilightbox = box; + } + } + } + /* + Regular blocks + */ + else + { + for(u16 i=0; i<6; i++) + { + v3f dir_f = v3f(dirs[i].X, + dirs[i].Y, dirs[i].Z); + v3f centerpoint = npf + dir_f * BS/2; + f32 distance = + (centerpoint - camera_position).getLength(); + + if(distance < mindistance) + { + core::CMatrix4 m; + m.buildRotateFromTo(v3f(0,0,1), dir_f); + + // This is the back face + v3f corners[2] = { + v3f(BS/2, BS/2, BS/2), + v3f(-BS/2, -BS/2, BS/2+d) + }; + + for(u16 j=0; j<2; j++) + { + m.rotateVect(corners[j]); + corners[j] += npf; + } + + core::aabbox3d facebox(corners[0]); + facebox.addInternalPoint(corners[1]); + + if(facebox.intersectsWithLine(shootline)) + { + nodefound = true; + nodepos = np; + neighbourpos = np + dirs[i]; + mindistance = distance; + + //nodehilightbox = facebox; + + const float d = 0.502; + core::aabbox3d nodebox + (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); + v3f nodepos_f = intToFloat(nodepos, BS); + nodebox.MinEdge += nodepos_f; + nodebox.MaxEdge += nodepos_f; + nodehilightbox = nodebox; + } + } // if distance < mindistance + } // for dirs + } // regular block + } // for coords +} + int main(int argc, char *argv[]) { /* @@ -2148,30 +2401,14 @@ int main(int argc, char *argv[]) //video::SColor skycolor = video::SColor(255,90,140,200); //video::SColor skycolor = video::SColor(255,166,202,244); - video::SColor skycolor = video::SColor(255,120,185,244); + //video::SColor skycolor = video::SColor(255,120,185,244); + video::SColor skycolor = video::SColor(255,140,186,250); camera->setFOV(FOV_ANGLE); // Just so big a value that everything rendered is visible camera->setFarValue(100000*BS); - /* - Lighting test code. Doesn't quite work this way. - The CPU-computed lighting is good. - */ - - /* - smgr->addLightSceneNode(NULL, - v3f(0, BS*1000000, 0), - video::SColorf(0.3,0.3,0.3), - BS*10000000); - - smgr->setAmbientLight(video::SColorf(0.0, 0.0, 0.0)); - - scene::ILightSceneNode *light = smgr->addLightSceneNode(camera, - v3f(0, 0, 0), video::SColorf(0.5,0.5,0.5), BS*4); - */ - f32 camera_yaw = 0; // "right/left" f32 camera_pitch = 0; // "up/down" @@ -2226,6 +2463,8 @@ int main(int argc, char *argv[]) core::list frametime_log; + float damage_flash_timer = 0; + /* Main loop */ @@ -2453,6 +2692,16 @@ int main(int argc, char *argv[]) ); client.setPlayerControl(control); } + + /* + Run server + */ + + if(server != NULL) + { + //TimeTaker timer("server->step(dtime)"); + server->step(dtime); + } /* Process environment @@ -2464,12 +2713,28 @@ int main(int argc, char *argv[]) //client.step(dtime_avg1); } - if(server != NULL) + // Read client events + for(;;) { - //TimeTaker timer("server->step(dtime)"); - server->step(dtime); + ClientEvent event = client.getClientEvent(); + if(event.type == CE_NONE) + { + break; + } + else if(event.type == CE_PLAYER_DAMAGE) + { + //u16 damage = event.player_damage.amount; + //dstream<<"Player damage: "<getRightClicked()) { std::cout<getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN) - { - } - /* - Otherwise pass the event to the server as-is - */ - else - { - client.clickObject(1, selected_object->getBlock()->getPos(), - selected_object->getId(), g_selected_item); - } -#endif } } else // selected_object == NULL @@ -2666,205 +2915,13 @@ int main(int argc, char *argv[]) v3s16 nodepos; v3s16 neighbourpos; core::aabbox3d nodehilightbox; - f32 mindistance = BS * 1001; - - v3s16 pos_i = floatToInt(player_position, BS); - - /*std::cout<<"pos_i=("<0 ? a : 1); - s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1); - s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1); - - for(s16 y = ystart; y <= yend; y++) - for(s16 z = zstart; z <= zend; z++) - for(s16 x = xstart; x <= xend; x++) - { - MapNode n; - try - { - n = client.getNode(v3s16(x,y,z)); - if(content_pointable(n.d) == false) - continue; - } - catch(InvalidPositionException &e) - { - continue; - } - - v3s16 np(x,y,z); - v3f npf = intToFloat(np, BS); - - f32 d = 0.01; - - v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - - /* - Meta-objects - */ - if(n.d == CONTENT_TORCH) - { - v3s16 dir = unpackDir(n.dir); - v3f dir_f = v3f(dir.X, dir.Y, dir.Z); - dir_f *= BS/2 - BS/6 - BS/20; - v3f cpf = npf + dir_f; - f32 distance = (cpf - camera_position).getLength(); - - core::aabbox3d box; - - // bottom - if(dir == v3s16(0,-1,0)) - { - box = core::aabbox3d( - npf - v3f(BS/6, BS/2, BS/6), - npf + v3f(BS/6, -BS/2+BS/3*2, BS/6) - ); - } - // top - else if(dir == v3s16(0,1,0)) - { - box = core::aabbox3d( - npf - v3f(BS/6, -BS/2+BS/3*2, BS/6), - npf + v3f(BS/6, BS/2, BS/6) - ); - } - // side - else - { - box = core::aabbox3d( - cpf - v3f(BS/6, BS/3, BS/6), - cpf + v3f(BS/6, BS/3, BS/6) - ); - } - - if(distance < mindistance) - { - if(box.intersectsWithLine(shootline)) - { - nodefound = true; - nodepos = np; - neighbourpos = np; - mindistance = distance; - nodehilightbox = box; - } - } - } - else if(n.d == CONTENT_SIGN_WALL) - { - v3s16 dir = unpackDir(n.dir); - v3f dir_f = v3f(dir.X, dir.Y, dir.Z); - dir_f *= BS/2 - BS/6 - BS/20; - v3f cpf = npf + dir_f; - f32 distance = (cpf - camera_position).getLength(); - - v3f vertices[4] = - { - v3f(BS*0.42,-BS*0.35,-BS*0.4), - v3f(BS*0.49, BS*0.35, BS*0.4), - }; - - for(s32 i=0; i<2; i++) - { - if(dir == v3s16(1,0,0)) - vertices[i].rotateXZBy(0); - if(dir == v3s16(-1,0,0)) - vertices[i].rotateXZBy(180); - if(dir == v3s16(0,0,1)) - vertices[i].rotateXZBy(90); - if(dir == v3s16(0,0,-1)) - vertices[i].rotateXZBy(-90); - if(dir == v3s16(0,-1,0)) - vertices[i].rotateXYBy(-90); - if(dir == v3s16(0,1,0)) - vertices[i].rotateXYBy(90); - - vertices[i] += npf; - } - - core::aabbox3d box; - - box = core::aabbox3d(vertices[0]); - box.addInternalPoint(vertices[1]); - - if(distance < mindistance) - { - if(box.intersectsWithLine(shootline)) - { - nodefound = true; - nodepos = np; - neighbourpos = np; - mindistance = distance; - nodehilightbox = box; - } - } - } - /* - Regular blocks - */ - else - { - for(u16 i=0; i<6; i++) - { - v3f dir_f = v3f(dirs[i].X, - dirs[i].Y, dirs[i].Z); - v3f centerpoint = npf + dir_f * BS/2; - f32 distance = - (centerpoint - camera_position).getLength(); - - if(distance < mindistance) - { - core::CMatrix4 m; - m.buildRotateFromTo(v3f(0,0,1), dir_f); - - // This is the back face - v3f corners[2] = { - v3f(BS/2, BS/2, BS/2), - v3f(-BS/2, -BS/2, BS/2+d) - }; - - for(u16 j=0; j<2; j++) - { - m.rotateVect(corners[j]); - corners[j] += npf; - } - - core::aabbox3d facebox(corners[0]); - facebox.addInternalPoint(corners[1]); - - if(facebox.intersectsWithLine(shootline)) - { - nodefound = true; - nodepos = np; - neighbourpos = np + dirs[i]; - mindistance = distance; - - //nodehilightbox = facebox; - - const float d = 0.502; - core::aabbox3d nodebox - (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d); - v3f nodepos_f = intToFloat(nodepos, BS); - nodebox.MinEdge += nodepos_f; - nodebox.MaxEdge += nodepos_f; - nodehilightbox = nodebox; - } - } // if distance < mindistance - } // for dirs - } // regular block - } // for coords + getPointedNode(player_position, + camera_direction, camera_position, + nodefound, shootline, + nodepos, neighbourpos, + nodehilightbox, d); + static float nodig_delay_counter = 0.0; if(nodefound) @@ -3430,10 +3487,26 @@ int main(int argc, char *argv[]) */ { draw_hotbar(driver, font, v2s32(displaycenter.X, screensize.Y), - hotbar_imagesize, hotbar_itemcount, &local_inventory); + hotbar_imagesize, hotbar_itemcount, &local_inventory, + client.getHP()); + } + + /* + Damage flash + */ + if(damage_flash_timer > 0.0) + { + damage_flash_timer -= dtime; + + video::SColor color(128,255,0,0); + driver->draw2DRectangle(color, + core::rect(0,0,screensize.X,screensize.Y), + NULL); } - // End drawing + /* + End scene + */ { TimeTaker timer("endScene"); driver->endScene(); diff --git a/src/map.cpp b/src/map.cpp index 2a92f6733072..7e4fc4f47313 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -37,8 +37,8 @@ Map::Map(std::ostream &dout): m_dout(dout), m_sector_cache(NULL) { - m_sector_mutex.Init(); - assert(m_sector_mutex.IsInitialized()); + /*m_sector_mutex.Init(); + assert(m_sector_mutex.IsInitialized());*/ } Map::~Map() @@ -104,7 +104,7 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p) MapSector * Map::getSectorNoGenerateNoEx(v2s16 p) { - JMutexAutoLock lock(m_sector_mutex); + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out return getSectorNoGenerateNoExNoLock(p); } @@ -1347,7 +1347,7 @@ bool Map::dayNightDiffed(v3s16 blockpos) */ void Map::timerUpdate(float dtime) { - JMutexAutoLock lock(m_sector_mutex); + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out core::map::Iterator si; @@ -1397,7 +1397,7 @@ void Map::deleteSectors(core::list &list, bool only_blocks) u32 Map::deleteUnusedSectors(float timeout, bool only_blocks, core::list *deleted_blocks) { - JMutexAutoLock lock(m_sector_mutex); + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out core::list sector_deletion_queue; core::map::Iterator i = m_sectors.getIterator(); @@ -2163,6 +2163,18 @@ void addRandomObjects(MapBlock *block) block->m_static_objects.insert(0, s_obj); delete obj; } + if(myrand() % 300 == 0) + { + v3f pos_f = intToFloat(p+block->getPosRelative(), BS); + pos_f.Y -= BS*0.4; + ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f); + std::string data = obj->getStaticData(); + StaticObject s_obj(obj->getType(), + obj->getBasePosition(), data); + // Add one + block->m_static_objects.insert(0, s_obj); + delete obj; + } } } } @@ -4714,7 +4726,7 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) // This won't work if proper generation is disabled if(m_chunksize == 0) return WATER_LEVEL+2; - double level = base_rock_level_2d(m_seed, p2d); + double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT; return (s16)level; } @@ -4794,7 +4806,7 @@ void ServerMap::save(bool only_changed) u32 block_count = 0; { //sectorlock - JMutexAutoLock lock(m_sector_mutex); + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out core::map::Iterator i = m_sectors.getIterator(); for(; i.atEnd() == false; i++) @@ -4856,7 +4868,7 @@ void ServerMap::loadAll() dstream<::Node *n = m_sectors.find(p2d); @@ -5435,7 +5447,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is) { sector = new ClientMapSector(this, p2d); { - JMutexAutoLock lock(m_sector_mutex); + //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out m_sectors.insert(p2d, sector); } } diff --git a/src/map.h b/src/map.h index 206dc7d7ba31..1cd021f52b9e 100644 --- a/src/map.h +++ b/src/map.h @@ -287,6 +287,11 @@ class Map : public NodeContainer void removeNodeMetadata(v3s16 p); void nodeMetadataStep(float dtime, core::map &changed_blocks); + + /* + Misc. + */ + core::map *getSectorsPtr(){return &m_sectors;} /* Variables @@ -298,16 +303,13 @@ class Map : public NodeContainer core::map m_event_receivers; - // Mutex is important because on client map is accessed asynchronously core::map m_sectors; - JMutex m_sector_mutex; + //JMutex m_sector_mutex; // Be sure to set this to NULL when the cached sector is deleted MapSector *m_sector_cache; v2s16 m_sector_cache_p; - //WrapperHeightmap m_hwrapper; - // Queued transforming water nodes UniqueQueue m_transforming_liquid; }; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 38c081eec9d5..5b8bc7b9f807 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -1924,9 +1924,19 @@ void MapBlock::serialize(std::ostream &os, u8 version) */ if(version >= 14) { - std::ostringstream oss(std::ios_base::binary); - m_node_metadata.serialize(oss); - os< *collision_info) { v3f position = getPosition(); v3f oldpos = position; @@ -530,9 +538,23 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) */ if(other_axes_overlap && main_axis_collides) { + v3f old_speed = m_speed; + m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; position -= position.dotProduct(dirs[i]) * dirs[i]; position += oldpos.dotProduct(dirs[i]) * dirs[i]; + + if(collision_info) + { + // Report fall collision + if(old_speed.Y < m_speed.Y - 0.1) + { + CollisionInfo info; + info.t = COLLISION_FALL; + info.speed = m_speed.Y - old_speed.Y; + collision_info->push_back(info); + } + } } } @@ -617,6 +639,11 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) setPosition(position); } +void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) +{ + move(dtime, map, pos_max_d, NULL); +} + void LocalPlayer::applyControl(float dtime) { // Clear stuff diff --git a/src/player.h b/src/player.h index 2eaeaae9a6cf..03fba1e2c0b2 100644 --- a/src/player.h +++ b/src/player.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include "inventory.h" +#include "collision.h" #define PLAYERNAME_SIZE 20 @@ -124,6 +125,8 @@ class Player bool craftresult_is_preview; + u16 hp; + u16 peer_id; protected: @@ -325,6 +328,8 @@ class LocalPlayer : public Player return true; } + void move(f32 dtime, Map &map, f32 pos_max_d, + core::list *collision_info); void move(f32 dtime, Map &map, f32 pos_max_d); void applyControl(float dtime); diff --git a/src/serialization.cpp b/src/serialization.cpp index c324ca0fdb13..6a43d9190f60 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -105,6 +105,12 @@ void compressZlib(SharedBuffer data, std::ostream &os) } +void compressZlib(const std::string &data, std::ostream &os) +{ + SharedBuffer databuf((u8*)data.c_str(), data.size()); + compressZlib(databuf, os); +} + void decompressZlib(std::istream &is, std::ostream &os) { z_stream z; diff --git a/src/serialization.h b/src/serialization.h index c87162e69ea6..c7cafc5d1930 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -48,17 +48,23 @@ with this program; if not, write to the Free Software Foundation, Inc., 13: (dev) Mapgen v2 14: (dev) NodeMetadata 15: (dev) StaticObjects + 16: (dev) larger maximum size of node metadata, and compression */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST 15 +#define SER_FMT_VER_HIGHEST 16 // Lowest supported serialization version #define SER_FMT_VER_LOWEST 0 #define ser_ver_supported(v) (v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST) +void compressZlib(SharedBuffer data, std::ostream &os); +void compressZlib(const std::string &data, std::ostream &os); +void decompressZlib(std::istream &is, std::ostream &os); + void compress(SharedBuffer data, std::ostream &os, u8 version); +//void compress(const std::string &data, std::ostream &os, u8 version); void decompress(std::istream &is, std::ostream &os, u8 version); /*class Serializable diff --git a/src/server.cpp b/src/server.cpp index 154603a47099..ee7a035e6bda 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -959,6 +959,8 @@ Server::Server( Server::~Server() { + dstream<<"Server::~Server()"<serialization_version == SER_FMT_VER_INVALID) continue; - SendChatMessage(client->peer_id, line); + try{ + SendChatMessage(client->peer_id, line); + } + catch(con::PeerNotFoundException &e) + {} } } /* Save players */ + dstream<<"Server: Saving players"<updateName((const char*)&data[3]); }*/ - - // Now answer with a TOCLIENT_INIT - SharedBuffer reply(2+1+6+8); - writeU16(&reply[0], TOCLIENT_INIT); - writeU8(&reply[2], deployed); - writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); - //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); - - // Send as reliable - m_con.Send(peer_id, 0, reply, true); + /* + Answer with a TOCLIENT_INIT + */ + { + SharedBuffer reply(2+1+6+8); + writeU16(&reply[0], TOCLIENT_INIT); + writeU8(&reply[2], deployed); + writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS)); + //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed()); + writeU64(&reply[2+1+6], 0); // no seed + + // Send as reliable + m_con.Send(peer_id, 0, reply, true); + } + + /* + Send complete position information + */ + SendMovePlayer(player); return; } + if(command == TOSERVER_INIT2) { derr_server<id); SendInventory(peer->id); + + // Send HP + { + Player *player = m_env.getPlayer(peer_id); + SendPlayerHP(player); + } // Send time of day { @@ -2005,6 +2026,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Add to inventory and send inventory ilist->addItem(item); + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } @@ -2026,7 +2048,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ u8 button = readU8(&data[2]); u16 id = readS16(&data[3]); - //u16 item_i = readU16(&data[11]); + u16 item_i = readU16(&data[11]); ServerActiveObject *obj = m_env.getActiveObject(id); @@ -2066,11 +2088,42 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { // Add to inventory and send inventory ilist->addItem(item); + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); // Remove object from environment obj->m_removed = true; } + else + { + /* + Item cannot be picked up. Punch it instead. + */ + + ToolItem *titem = NULL; + std::string toolname = ""; + + InventoryList *mlist = player->inventory.getList("main"); + if(mlist != NULL) + { + InventoryItem *item = mlist->getItem(item_i); + if(item && (std::string)item->getName() == "ToolItem") + { + titem = (ToolItem*)item; + toolname = titem->getToolName(); + } + } + + u16 wear = obj->punch(toolname); + + if(titem) + { + bool weared_out = titem->addWear(wear); + if(weared_out) + mlist->deleteItem(item_i); + SendInventory(player->peer_id); + } + } } } } @@ -2276,6 +2329,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) player->inventory.addItem("main", item); // Send inventory + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } } @@ -2380,6 +2434,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else mitem->remove(1); // Send inventory + UpdateCrafting(peer_id); SendInventory(peer_id); } @@ -2492,6 +2547,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) item->remove(dropcount); // Send inventory + UpdateCrafting(peer_id); SendInventory(peer_id); } } @@ -2711,6 +2767,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else { // Send inventory + UpdateCrafting(player->peer_id); SendInventory(player->peer_id); } } @@ -2856,6 +2913,36 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } } } + else if(command == TOSERVER_DAMAGE) + { + if(g_settings.getBool("enable_damage")) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u8 damage = readU8(is); + if(player->hp > damage) + { + player->hp -= damage; + } + else + { + player->hp = 0; + + dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies" + <setPosition(pos); + player->hp = 20; + SendMovePlayer(player); + SendPlayerHP(player); + + //TODO: Throw items around + } + } + + SendPlayerHP(player); + } else { derr_server<<"WARNING: Server::ProcessData(): Ignoring " @@ -2914,6 +3001,7 @@ void Server::inventoryModified(InventoryContext *c, std::string id) { assert(c->current_player); // Send inventory + UpdateCrafting(c->current_player->peer_id); SendInventory(c->current_player->peer_id); return; } @@ -3016,6 +3104,29 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) m_peer_change_queue.push_back(c); } +/* + Static send methods +*/ + +void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_HP); + writeU8(os, hp); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + +/* + Non-static send methods +*/ + void Server::SendObjectData(float dtime) { DSTACK(__FUNCTION_NAME); @@ -3082,51 +3193,351 @@ void Server::SendInventory(u16 peer_id) assert(player); /* - Calculate crafting stuff + Serialize it */ - if(g_settings.getBool("creative_mode") == false) + + std::ostringstream os; + //os.imbue(std::locale("C")); + + player->inventory.serialize(os); + + std::string s = os.str(); + + SharedBuffer data(s.size()+2); + writeU16(&data[0], TOCLIENT_INVENTORY); + memcpy(&data[2], s.c_str(), s.size()); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendChatMessage(u16 peer_id, const std::wstring &message) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + u8 buf[12]; + + // Write command + writeU16(buf, TOCLIENT_CHAT_MESSAGE); + os.write((char*)buf, 2); + + // Write length + writeU16(buf, message.size()); + os.write((char*)buf, 2); + + // Write string + for(u32 i=0; iinventory.getList("craft"); - InventoryList *rlist = player->inventory.getList("craftresult"); + u16 w = message[i]; + writeU16(buf, w); + os.write((char*)buf, 2); + } + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} - if(rlist->getUsedSlots() == 0) - player->craftresult_is_preview = true; +void Server::BroadcastChatMessage(const std::wstring &message) +{ + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + // Get client and check that it is valid + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; - if(rlist && player->craftresult_is_preview) - { - rlist->clearItems(); - } - if(clist && rlist && player->craftresult_is_preview) - { - InventoryItem *items[9]; - for(u16 i=0; i<9; i++) - { - items[i] = clist->getItem(i); - } - - bool found = false; + SendChatMessage(client->peer_id, message); + } +} - // Wood - if(!found) +void Server::SendPlayerHP(Player *player) +{ + SendHP(m_con, player->peer_id, player->hp); +} + +void Server::SendMovePlayer(Player *player) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_MOVE_PLAYER); + writeV3F1000(os, player->getPosition()); + writeF1000(os, player->getPitch()); + writeF1000(os, player->getYaw()); + + { + v3f pos = player->getPosition(); + f32 pitch = player->getPitch(); + f32 yaw = player->getYaw(); + dstream<<"Server sending TOCLIENT_MOVE_PLAYER" + <<" pos=("<push_back(client->peer_id); + continue; + } + } + } + + // Create packet + u32 replysize = 8 + MapNode::serializedLength(client->serialization_version); + SharedBuffer reply(replysize); + writeU16(&reply[0], TOCLIENT_ADDNODE); + writeS16(&reply[2], p.X); + writeS16(&reply[4], p.Y); + writeS16(&reply[6], p.Z); + n.serialize(&reply[8], client->serialization_version); + + // Send as reliable + m_con.Send(client->peer_id, 0, reply, true); + } +} + +void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) +{ + DSTACK(__FUNCTION_NAME); + /* + Create a packet with the block in the right format + */ + + std::ostringstream os(std::ios_base::binary); + block->serialize(os, ver); + std::string s = os.str(); + SharedBuffer blockdata((u8*)s.c_str(), s.size()); + + u32 replysize = 8 + blockdata.getSize(); + SharedBuffer reply(replysize); + v3s16 p = block->getPos(); + writeU16(&reply[0], TOCLIENT_BLOCKDATA); + writeS16(&reply[2], p.X); + writeS16(&reply[4], p.Y); + writeS16(&reply[6], p.Z); + memcpy(&reply[8], *blockdata, blockdata.getSize()); + + /*dstream<<"Server: Sending block ("< queue; + + s32 total_sending = 0; + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + assert(client->peer_id == i.getNode()->getKey()); + + total_sending += client->SendingCount(); + + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + client->GetNextBlocks(this, dtime, queue); + } + + // Sort. + // Lowest priority number comes first. + // Lowest is most important. + queue.sort(); + + for(u32 i=0; i= g_settings.getS32 + ("max_simultaneous_block_sends_server_total")) + break; + + PrioritySortedBlockTransfer q = queue[i]; + + MapBlock *block = NULL; + try + { + block = m_env.getMap().getBlockNoCreate(q.pos); + } + catch(InvalidPositionException &e) + { + continue; + } + + RemoteClient *client = getClient(q.peer_id); + + SendBlockNoLock(q.peer_id, block, client->serialization_version); + + client->SentBlock(q.pos); + + total_sending++; + } +} + +/* + Something random +*/ + +void Server::UpdateCrafting(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + + Player* player = m_env.getPlayer(peer_id); + assert(player); + + /* + Calculate crafting stuff + */ + if(g_settings.getBool("creative_mode") == false) + { + InventoryList *clist = player->inventory.getList("craft"); + InventoryList *rlist = player->inventory.getList("craftresult"); + + if(rlist->getUsedSlots() == 0) + player->craftresult_is_preview = true; + + if(rlist && player->craftresult_is_preview) + { + rlist->clearItems(); + } + if(clist && rlist && player->craftresult_is_preview) + { + InventoryItem *items[9]; + for(u16 i=0; i<9; i++) + { + items[i] = clist->getItem(i); + } + + bool found = false; + + // Wood + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new MaterialItem(CONTENT_WOOD, 4)); + found = true; + } + } + + // Stick + if(!found) + { + ItemSpec specs[9]; + specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new CraftItem("Stick", 4)); + found = true; } } @@ -3226,7 +3637,7 @@ void Server::SendInventory(u16 peer_id) } } - // Wooden showel + // Wooden shovel if(!found) { ItemSpec specs[9]; @@ -3240,7 +3651,7 @@ void Server::SendInventory(u16 peer_id) } } - // Stone showel + // Stone shovel if(!found) { ItemSpec specs[9]; @@ -3254,7 +3665,7 @@ void Server::SendInventory(u16 peer_id) } } - // Steel showel + // Steel shovel if(!found) { ItemSpec specs[9]; @@ -3316,6 +3727,48 @@ void Server::SendInventory(u16 peer_id) } } + // Wooden sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("WSword", 0)); + found = true; + } + } + + // Stone sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("STSword", 0)); + found = true; + } + } + + // Steel sword + if(!found) + { + ItemSpec specs[9]; + specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot"); + specs[7] = ItemSpec(ITEM_CRAFT, "Stick"); + if(checkItemCombination(items, specs)) + { + rlist->addItem(new ToolItem("SteelSword", 0)); + found = true; + } + } + // Chest if(!found) { @@ -3376,264 +3829,8 @@ void Server::SendInventory(u16 peer_id) } } // if creative_mode == false - - /* - Serialize it - */ - - std::ostringstream os; - //os.imbue(std::locale("C")); - - player->inventory.serialize(os); - - std::string s = os.str(); - - SharedBuffer data(s.size()+2); - writeU16(&data[0], TOCLIENT_INVENTORY); - memcpy(&data[2], s.c_str(), s.size()); - - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} - -void Server::SendChatMessage(u16 peer_id, const std::wstring &message) -{ - DSTACK(__FUNCTION_NAME); - - std::ostringstream os(std::ios_base::binary); - u8 buf[12]; - - // Write command - writeU16(buf, TOCLIENT_CHAT_MESSAGE); - os.write((char*)buf, 2); - - // Write length - writeU16(buf, message.size()); - os.write((char*)buf, 2); - - // Write string - for(u32 i=0; i data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); } -void Server::BroadcastChatMessage(const std::wstring &message) -{ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - SendChatMessage(client->peer_id, message); - } -} - -void Server::sendRemoveNode(v3s16 p, u16 ignore_id, - core::list *far_players, float far_d_nodes) -{ - float maxd = far_d_nodes*BS; - v3f p_f = intToFloat(p, BS); - - // Create packet - u32 replysize = 8; - SharedBuffer reply(replysize); - writeU16(&reply[0], TOCLIENT_REMOVENODE); - writeS16(&reply[2], p.X); - writeS16(&reply[4], p.Y); - writeS16(&reply[6], p.Z); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; - - if(far_players) - { - // Get player - Player *player = m_env.getPlayer(client->peer_id); - if(player) - { - // If player is far away, only set modified blocks not sent - v3f player_pos = player->getPosition(); - if(player_pos.getDistanceFrom(p_f) > maxd) - { - far_players->push_back(client->peer_id); - continue; - } - } - } - - // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); - } -} - -void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, - core::list *far_players, float far_d_nodes) -{ - float maxd = far_d_nodes*BS; - v3f p_f = intToFloat(p, BS); - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; - - if(far_players) - { - // Get player - Player *player = m_env.getPlayer(client->peer_id); - if(player) - { - // If player is far away, only set modified blocks not sent - v3f player_pos = player->getPosition(); - if(player_pos.getDistanceFrom(p_f) > maxd) - { - far_players->push_back(client->peer_id); - continue; - } - } - } - - // Create packet - u32 replysize = 8 + MapNode::serializedLength(client->serialization_version); - SharedBuffer reply(replysize); - writeU16(&reply[0], TOCLIENT_ADDNODE); - writeS16(&reply[2], p.X); - writeS16(&reply[4], p.Y); - writeS16(&reply[6], p.Z); - n.serialize(&reply[8], client->serialization_version); - - // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); - } -} - -void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) -{ - DSTACK(__FUNCTION_NAME); - /* - Create a packet with the block in the right format - */ - - std::ostringstream os(std::ios_base::binary); - block->serialize(os, ver); - std::string s = os.str(); - SharedBuffer blockdata((u8*)s.c_str(), s.size()); - - u32 replysize = 8 + blockdata.getSize(); - SharedBuffer reply(replysize); - v3s16 p = block->getPos(); - writeU16(&reply[0], TOCLIENT_BLOCKDATA); - writeS16(&reply[2], p.X); - writeS16(&reply[4], p.Y); - writeS16(&reply[6], p.Z); - memcpy(&reply[8], *blockdata, blockdata.getSize()); - - /*dstream<<"Sending block ("< queue; - - s32 total_sending = 0; - - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - - total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); - } - - // Sort. - // Lowest priority number comes first. - // Lowest is most important. - queue.sort(); - - for(u32 i=0; i= g_settings.getS32 - ("max_simultaneous_block_sends_server_total")) - break; - - PrioritySortedBlockTransfer q = queue[i]; - - MapBlock *block = NULL; - try - { - block = m_env.getMap().getBlockNoCreate(q.pos); - } - catch(InvalidPositionException &e) - { - continue; - } - - RemoteClient *client = getClient(q.peer_id); - - SendBlockNoLock(q.peer_id, block, client->serialization_version); - - client->SentBlock(q.pos); - - total_sending++; - } -} - - RemoteClient* Server::getClient(u16 peer_id) { DSTACK(__FUNCTION_NAME); @@ -3682,14 +3879,24 @@ void setCreativeInventory(Player *player) { player->resetInventory(); - // Give some good picks + // Give some good tools { - InventoryItem *item = new ToolItem("STPick", 0); + InventoryItem *item = new ToolItem("MesePick", 0); void* r = player->inventory.addItem("main", item); assert(r == NULL); } { - InventoryItem *item = new ToolItem("MesePick", 0); + InventoryItem *item = new ToolItem("SteelPick", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelAxe", 0); + void* r = player->inventory.addItem("main", item); + assert(r == NULL); + } + { + InventoryItem *item = new ToolItem("SteelShovel", 0); void* r = player->inventory.addItem("main", item); assert(r == NULL); } @@ -3756,6 +3963,52 @@ void setCreativeInventory(Player *player) }*/ } +v3f findSpawnPos(ServerMap &map) +{ + v2s16 nodepos; + s16 groundheight = 0; + + // Try to find a good place a few times + for(s32 i=0; i<1000; i++) + { + s32 range = 1 + i; + // We're going to try to throw the player to this position + nodepos = v2s16(-range + (myrand()%(range*2)), + -range + (myrand()%(range*2))); + v2s16 sectorpos = getNodeSectorPos(nodepos); + // Get sector (NOTE: Don't get because it's slow) + //m_env.getMap().emergeSector(sectorpos); + // Get ground height at point (fallbacks to heightmap function) + groundheight = map.findGroundLevel(nodepos); + // Don't go underwater + if(groundheight < WATER_LEVEL) + { + //dstream<<"-> Underwater"< WATER_LEVEL + 4) + { + //dstream<<"-> Underwater"<getName()<<"\""<setPosition(intToFloat(v3s16( - 0, - 45, //64, - 0 - ), BS)); -#endif -#if 1 - s16 groundheight = 0; -#if 1 - // Try to find a good place a few times - for(s32 i=0; i<1000; i++) - { - s32 range = 1 + i; - // We're going to try to throw the player to this position - nodepos = v2s16(-range + (myrand()%(range*2)), - -range + (myrand()%(range*2))); - v2s16 sectorpos = getNodeSectorPos(nodepos); - // Get sector (NOTE: Don't get because it's slow) - //m_env.getMap().emergeSector(sectorpos); - // Get ground height at point (fallbacks to heightmap function) - groundheight = m_env.getServerMap().findGroundLevel(nodepos); - // Don't go underwater - if(groundheight < WATER_LEVEL) - { - //dstream<<"-> Underwater"< WATER_LEVEL + 4) - { - //dstream<<"-> Underwater"<setPosition(intToFloat(v3s16( - nodepos.X, - groundheight + 5, // Accomodate mud - nodepos.Y - ), BS)); -#endif + player->setPosition(pos); /* Add player to environment diff --git a/src/server.h b/src/server.h index 9059e91b8791..cba5fc2ce1ae 100644 --- a/src/server.h +++ b/src/server.h @@ -33,6 +33,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "inventory.h" +/* + Some random functions +*/ +v3f findSpawnPos(ServerMap &map); + +/* + A structure containing the data needed for queueing the fetching + of blocks. +*/ struct QueuedBlockEmerge { v3s16 pos; @@ -397,12 +406,24 @@ class Server : public con::PeerHandler, public MapEventReceiver, void peerAdded(con::Peer *peer); void deletingPeer(con::Peer *peer, bool timeout); + /* + Static send methods + */ + + static void SendHP(con::Connection &con, u16 peer_id, u8 hp); + + /* + Non-static send methods + */ + // Envlock and conlock should be locked when calling these void SendObjectData(float dtime); void SendPlayerInfos(); void SendInventory(u16 peer_id); void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); + void SendPlayerHP(Player *player); + void SendMovePlayer(Player *player); /* Send a node removal/addition event to all clients except ignore_id. Additionally, if far_players!=NULL, players further away than @@ -418,6 +439,12 @@ class Server : public con::PeerHandler, public MapEventReceiver, // Sends blocks to clients void SendBlocks(float dtime); + + /* + Something random + */ + + void UpdateCrafting(u16 peer_id); // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id); diff --git a/src/serverobject.cpp b/src/serverobject.cpp index 5d391dbcf359..30234f7e9892 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -451,4 +451,219 @@ InventoryItem* RatSAO::createPickedUpItem() return item; } +/* + Oerkki1SAO +*/ + +// Prototype +Oerkki1SAO proto_Oerkki1SAO(NULL, 0, v3f(0,0,0)); + +Oerkki1SAO::Oerkki1SAO(ServerEnvironment *env, u16 id, v3f pos): + ServerActiveObject(env, id, pos), + m_is_active(false), + m_speed_f(0,0,0) +{ + ServerActiveObject::registerType(getType(), create); + + m_oldpos = v3f(0,0,0); + m_last_sent_position = v3f(0,0,0); + m_yaw = 0; + m_counter1 = 0; + m_counter2 = 0; + m_age = 0; + m_touching_ground = false; + m_hp = 20; +} + +ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos, + const std::string &data) +{ + std::istringstream is(data, std::ios::binary); + // read version + u8 version = readU8(is); + // read hp + u8 hp = readU8(is); + // check if version is supported + if(version != 0) + return NULL; + Oerkki1SAO *o = new Oerkki1SAO(env, id, pos); + o->m_hp = hp; + return o; +} + +void Oerkki1SAO::step(float dtime, Queue &messages, + bool send_recommended) +{ + assert(m_env); + + if(m_is_active == false) + { + if(m_inactive_interval.step(dtime, 0.5)==false) + return; + } + + /* + The AI + */ + + m_age += dtime; + if(m_age > 60) + { + // Die + m_removed = true; + return; + } + + // Apply gravity + m_speed_f.Y -= dtime*9.81*BS; + + /* + Move around if some player is close + */ + bool player_is_close = false; + v3f near_player_pos; + // Check connected players + core::list players = m_env->getPlayers(true); + core::list::Iterator i; + for(i = players.begin(); + i != players.end(); i++) + { + Player *player = *i; + v3f playerpos = player->getPosition(); + if(m_base_position.getDistanceFrom(playerpos) < BS*15.0) + { + player_is_close = true; + near_player_pos = playerpos; + break; + } + } + + m_is_active = player_is_close; + + if(player_is_close == false) + { + m_speed_f.X = 0; + m_speed_f.Z = 0; + } + else + { + // Move around + + v3f ndir = near_player_pos - m_base_position; + ndir.Y = 0; + ndir /= ndir.getLength(); + f32 nyaw = 180./PI*atan2(ndir.Z,ndir.X); + if(nyaw < m_yaw - 180) + nyaw += 360; + else if(nyaw > m_yaw + 180) + nyaw -= 360; + m_yaw = 0.95*m_yaw + 0.05*nyaw; + m_yaw = wrapDegrees(m_yaw); + + v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); + f32 speed = 2*BS; + m_speed_f.X = speed * dir.X; + m_speed_f.Z = speed * dir.Z; + + if(m_touching_ground && (m_oldpos - m_base_position).getLength() + < dtime*speed/2) + { + m_counter1 -= dtime; + if(m_counter1 < 0.0) + { + m_counter1 += 1.0; + // Jump + m_speed_f.Y = 5.0*BS; + } + } + + { + m_counter2 -= dtime; + if(m_counter2 < 0.0) + { + m_counter2 += (float)(myrand()%100)/100*3.0; + //m_yaw += ((float)(myrand()%200)-100)/100*180; + m_yaw += ((float)(myrand()%200)-100)/100*90; + m_yaw = wrapDegrees(m_yaw); + } + } + } + + m_oldpos = m_base_position; + + /* + Move it, with collision detection + */ + + core::aabbox3d box(-BS/3.,0.0,-BS/3., BS/3.,BS*5./3.,BS/3.); + collisionMoveResult moveresult; + // Maximum movement without glitches + f32 pos_max_d = BS*0.25; + // Limit speed + if(m_speed_f.getLength()*dtime > pos_max_d) + m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime); + v3f pos_f = getBasePosition(); + v3f pos_f_old = pos_f; + moveresult = collisionMoveSimple(&m_env->getMap(), pos_max_d, + box, dtime, pos_f, m_speed_f); + m_touching_ground = moveresult.touching_ground; + + setBasePosition(pos_f); + + if(send_recommended == false) + return; + + if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS) + { + m_last_sent_position = pos_f; + + std::ostringstream os(std::ios::binary); + // command (0 = update position) + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + // yaw + writeF1000(os, m_yaw); + // create message and add to list + ActiveObjectMessage aom(getId(), false, os.str()); + messages.push_back(aom); + } +} + +std::string Oerkki1SAO::getClientInitializationData() +{ + std::ostringstream os(std::ios::binary); + // version + writeU8(os, 0); + // pos + writeV3F1000(os, m_base_position); + return os.str(); +} + +std::string Oerkki1SAO::getStaticData() +{ + //dstream<<__FUNCTION_NAME< &messages, + bool send_recommended); + std::string getClientInitializationData(); + std::string getStaticData(); + InventoryItem* createPickedUpItem(){return NULL;} + u16 punch(const std::string &toolname); +private: + bool m_is_active; + IntervalLimiter m_inactive_interval; + v3f m_speed_f; + v3f m_oldpos; + v3f m_last_sent_position; + float m_yaw; + float m_counter1; + float m_counter2; + float m_age; + bool m_touching_ground; + u8 m_hp; +}; + #endif diff --git a/src/test.cpp b/src/test.cpp index c7bffcdf3813..07ef772eff77 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -951,18 +951,18 @@ struct TestConnection assert(got_exception); } { - //u8 data1[1100]; - SharedBuffer data1(1100); - for(u16 i=0; i<1100; i++){ + const int datasize = 30000; + SharedBuffer data1(datasize); + for(u16 i=0; i20) + if(datasize>20) dstream<<"..."; dstream<