From 2419d0029ac36952aaa74b685d529a3592adb6aa Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 16 Oct 2011 22:39:35 +0300 Subject: [PATCH 01/40] Some more profiler stuff to get the hang on what really uses CPU --- src/content_sao.cpp | 11 ++++++++ src/environment.cpp | 14 ++++++----- src/game.cpp | 2 +- src/profiler.h | 61 +++++++++++++++++++++++++++++++++------------ src/server.cpp | 2 +- 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 1968b7b..9569b65 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "environment.h" #include "settings.h" +#include "profiler.h" core::map ServerActiveObject::m_types; @@ -137,6 +138,8 @@ ServerActiveObject* ItemSAO::create(ServerEnvironment *env, u16 id, v3f pos, void ItemSAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "ItemSAO::step avg", SPT_AVG); + assert(m_env); const float interval = 0.2; @@ -291,6 +294,8 @@ ServerActiveObject* RatSAO::create(ServerEnvironment *env, u16 id, v3f pos, void RatSAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "RatSAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -480,6 +485,8 @@ ServerActiveObject* Oerkki1SAO::create(ServerEnvironment *env, u16 id, v3f pos, void Oerkki1SAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "Oerkki1SAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -752,6 +759,8 @@ ServerActiveObject* FireflySAO::create(ServerEnvironment *env, u16 id, v3f pos, void FireflySAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "FireflySAO::step avg", SPT_AVG); + assert(m_env); if(m_is_active == false) @@ -1065,6 +1074,8 @@ static void explodeSquare(Map *map, v3s16 p0, v3s16 size) void MobV2SAO::step(float dtime, bool send_recommended) { + ScopeProfiler sp2(g_profiler, "MobV2SAO::step avg", SPT_AVG); + assert(m_env); Map *map = &m_env->getMap(); diff --git a/src/environment.cpp b/src/environment.cpp index 99dc6d6..47743bf 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -684,7 +684,7 @@ void ServerEnvironment::step(float dtime) Handle players */ { - ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); for(core::list::Iterator i = m_players.begin(); i != m_players.end(); i++) { @@ -726,7 +726,7 @@ void ServerEnvironment::step(float dtime) */ if(m_active_blocks_management_interval.step(dtime, 2.0)) { - ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG); /* Get player block positions */ @@ -803,7 +803,7 @@ void ServerEnvironment::step(float dtime) */ if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0)) { - ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG); float dtime = 1.0; @@ -842,7 +842,7 @@ void ServerEnvironment::step(float dtime) if(m_active_blocks_test_interval.step(dtime, 10.0)) { - ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG); //float dtime = 10.0; for(core::map::Iterator @@ -1045,8 +1045,10 @@ void ServerEnvironment::step(float dtime) Step active objects */ { - ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG); //TimeTaker timer("Step active objects"); + + g_profiler->avg("SEnv: num of objects", m_active_objects.size()); // This helps the objects to send data at the same time bool send_recommended = false; @@ -1086,7 +1088,7 @@ void ServerEnvironment::step(float dtime) */ if(m_object_management_interval.step(dtime, 0.5)) { - ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg", SPT_LOWPASS); + ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); /* Remove objects that satisfy (m_removed && m_known_by_count==0) */ diff --git a/src/game.cpp b/src/game.cpp index 276857f..e666e08 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1070,7 +1070,7 @@ void the_game( object_hit_delay_timer -= dtime; - g_profiler->add("Elapsed time", dtime * 1000); + g_profiler->add("Elapsed time", dtime); /* Log frametime for visualization diff --git a/src/profiler.h b/src/profiler.h index 8eaf18d..129118e 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -41,28 +41,48 @@ public: void add(const std::string &name, float value) { JMutexAutoLock lock(m_mutex); - core::map::Node *n = m_data.find(name); - if(n == NULL) { - m_data[name] = value; + /* No average shall have been used; mark add used as -2 */ + core::map::Node *n = m_avgcounts.find(name); + if(n == NULL) + m_avgcounts[name] = -2; + else{ + if(n->getValue() == -1) + n->setValue(-2); + assert(n->getValue() == -2); + } } - else { - n->setValue(n->getValue() + value); + core::map::Node *n = m_data.find(name); + if(n == NULL) + m_data[name] = value; + else + n->setValue(n->getValue() + value); } } - void lowpass(const std::string &name, float value, float factor) + void avg(const std::string &name, float value) { JMutexAutoLock lock(m_mutex); - core::map::Node *n = m_data.find(name); - if(n == NULL) { - m_data[name] = value; + core::map::Node *n = m_avgcounts.find(name); + if(n == NULL) + m_avgcounts[name] = 1; + else{ + /* No add shall have been used */ + assert(n->getValue() != -2); + if(n->getValue() <= 0) + n->setValue(1); + else + n->setValue(n->getValue() + 1); + } } - else { - n->setValue(n->getValue() * (1.0 - 1.0/factor) + value / factor); + core::map::Node *n = m_data.find(name); + if(n == NULL) + m_data[name] = value; + else + n->setValue(n->getValue() + value); } } @@ -75,6 +95,7 @@ public: { i.getNode()->setValue(0); } + m_avgcounts.clear(); } void print(std::ostream &o) @@ -85,6 +106,12 @@ public: i.atEnd() == false; i++) { std::string name = i.getNode()->getKey(); + int avgcount = 1; + core::map::Node *n = m_avgcounts.find(name); + if(n){ + if(n->getValue() >= 1) + avgcount = n->getValue(); + } o<<" "<getValue(); + o<<(i.getNode()->getValue() / avgcount); o< m_data; + core::map m_avgcounts; }; enum ScopeProfilerType{ SPT_ADD, - SPT_LOWPASS + SPT_AVG }; class ScopeProfiler @@ -138,14 +166,15 @@ public: { if(m_timer) { - u32 duration = m_timer->stop(true); + float duration_ms = m_timer->stop(true); + float duration = duration_ms / 1000.0; if(m_profiler){ switch(m_type){ case SPT_ADD: m_profiler->add(m_name, duration); break; - case SPT_LOWPASS: - m_profiler->lowpass(m_name, duration, 20.0); + case SPT_AVG: + m_profiler->avg(m_name, duration); break; } } diff --git a/src/server.cpp b/src/server.cpp index ed0c97c..14c019d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1214,7 +1214,7 @@ void Server::AsyncRunStep() JMutexAutoLock lock(m_env_mutex); // Step environment ScopeProfiler sp(g_profiler, "SEnv step"); - ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_LOWPASS); + ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG); m_env.step(dtime); } From 162619a426c9af15ac4868c9c7ce882f9932db38 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 16 Oct 2011 23:41:43 +0300 Subject: [PATCH 02/40] Fix to-transparend conversion of backgrounds of sprite image files that don't have an alpha channel --- src/tile.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/tile.cpp b/src/tile.cpp index 4e44132..e274d2d 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1092,22 +1092,23 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, core::dimension2d dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + // Blit + image->copyTo(baseimg); + + image->drop(); + for(u32 y=0; ygetPixel(x,y); + video::SColor c = baseimg->getPixel(x,y); u32 r = c.getRed(); u32 g = c.getGreen(); u32 b = c.getBlue(); if(!(r == r1 && g == g1 && b == b1)) continue; c.setAlpha(0); - image->setPixel(x,y,c); + baseimg->setPixel(x,y,c); } - // Blit - image->copyTo(baseimg); - - image->drop(); } } /* @@ -1149,11 +1150,16 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, { core::dimension2d dim = image->getDimension(); baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); + + // Blit + image->copyTo(baseimg); + + image->drop(); for(u32 y=0; ygetPixel(x,y); + video::SColor c = baseimg->getPixel(x,y); u32 r = c.getRed(); u32 g = c.getGreen(); u32 b = c.getBlue(); @@ -1161,12 +1167,8 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, !(r == r2 && g == g2 && b == b2)) continue; c.setAlpha(0); - image->setPixel(x,y,c); + baseimg->setPixel(x,y,c); } - // Blit - image->copyTo(baseimg); - - image->drop(); } } /* From 9ff806742655340a8e5abb8d5f91d32b89b9cd5f Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 00:08:27 +0300 Subject: [PATCH 03/40] Fix items showing up as sticks when placed in world --- src/content_cao.cpp | 72 ++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d3a1813..b17c281 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -208,6 +208,35 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr) // This is needed for changing the texture in the future m_node->setReadOnlyMaterials(true); updateNodePos(); + + /* + Update image of node + */ + + // Create an inventory item to see what is its image + std::istringstream is(m_inventorystring, std::ios_base::binary); + video::ITexture *texture = NULL; + try{ + InventoryItem *item = NULL; + item = InventoryItem::deSerialize(is); + infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" + < item="<getImage(); + delete item; + } + } + catch(SerializationError &e) + { + infostream<<"WARNING: "<<__FUNCTION_NAME + <<": error deSerializing inventorystring \"" + <getMaterial().setTexture(0, texture); } void ItemCAO::removeFromScene() @@ -289,49 +318,6 @@ void ItemCAO::initialize(const std::string &data) } updateNodePos(); - - /* - Update image of node - */ - - if(m_node == NULL) - return; - - scene::IMesh *mesh = m_node->getMesh(); - - if(mesh == NULL) - return; - - scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); - - if(buf == NULL) - return; - - // Create an inventory item to see what is its image - std::istringstream is(m_inventorystring, std::ios_base::binary); - video::ITexture *texture = NULL; - try{ - InventoryItem *item = NULL; - item = InventoryItem::deSerialize(is); - infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" - < item="<getImage(); - delete item; - } - } - catch(SerializationError &e) - { - infostream<<"WARNING: "<<__FUNCTION_NAME - <<": error deSerializing inventorystring \"" - <getMaterial().setTexture(0, texture); - } /* From 5f39885975dcd73a9589ce9be0aeb908eebc3450 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 01:03:45 +0300 Subject: [PATCH 04/40] Improve inventory debug output --- src/guiInventoryMenu.cpp | 21 ++++++----- src/inventory.cpp | 80 ++++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index 165d4d8..7d49aca 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "log.h" void drawInventoryItem(video::IVideoDriver *driver, gui::IGUIFont *font, @@ -326,11 +327,11 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(amount >= 0) { v2s32 p(event.MouseInput.X, event.MouseInput.Y); - //dstream<<"Mouse down at p=("<getList(s.listname); if(list_from == NULL) - dstream<<"from list doesn't exist"<getItem(m_selected_item->i) != NULL) { - dstream<<"Handing IACTION_MOVE to manager"<count = amount; a->from_inv = m_selected_item->inventoryname; @@ -408,7 +409,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) { if(!canTakeFocus(event.GUIEvent.Element)) { - dstream<<"GUIInventoryMenu: Not allowing focus change." + infostream<<"GUIInventoryMenu: Not allowing focus change." <current_player == NULL) + return "current_player=NULL"; + else + return std::string("current_player=") + c->current_player->getName(); +} + void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr) { -#if 1 - - /*dstream<<"from_inv="<getList(from_list); InventoryList *list_to = inv_to->getList(to_list); - /*dstream<<"list_from="<getItem(from_i)="<getItem(from_i) - <getItem(to_i)="<getItem(to_i) - <getItem(from_i) == NULL) { - dstream<<__FUNCTION_NAME<<": Operation not allowed " - <<"(the source item doesn't exist)" - < Date: Mon, 17 Oct 2011 01:58:38 +0300 Subject: [PATCH 06/40] Fix punching of oerkkis --- src/content_sao.cpp | 18 ++++++++++++++++-- src/content_sao.h | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 9569b65..61a2df2 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -684,11 +684,25 @@ std::string Oerkki1SAO::getStaticData() return os.str(); } -u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir) +u16 Oerkki1SAO::punch(const std::string &toolname, v3f dir, + const std::string &playername) { m_speed_f += dir*12*BS; - u16 amount = 20; + u16 amount = 5; + /* See tool names in inventory.h */ + if(toolname == "WSword") + amount = 10; + if(toolname == "STSword") + amount = 12; + if(toolname == "SteelSword") + amount = 16; + if(toolname == "STAxe") + amount = 7; + if(toolname == "SteelAxe") + amount = 9; + if(toolname == "SteelPick") + amount = 7; doDamage(amount); return 65536/100; } diff --git a/src/content_sao.h b/src/content_sao.h index a335c12..f0ebf4f 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -96,7 +96,8 @@ public: std::string getClientInitializationData(); std::string getStaticData(); InventoryItem* createPickedUpItem(){return NULL;} - u16 punch(const std::string &toolname, v3f dir); + u16 punch(const std::string &toolname, v3f dir, + const std::string &playername); bool isPeaceful(){return false;} private: void doDamage(u16 d); From 30c21b4abd1b7458c30f267e23c9bd003ed0bc0a Mon Sep 17 00:00:00 2001 From: celeron55 Date: Mon, 17 Oct 2011 10:46:16 +0300 Subject: [PATCH 07/40] Fix partly double printed debug.txt --- src/main.cpp | 10 +++++----- src/servermain.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 80daf74..be98367 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1074,15 +1074,15 @@ void drawMenuBackground(video::IVideoDriver* driver) } } -class DstreamLogOutput: public ILogOutput +class StderrLogOutput: public ILogOutput { public: /* line: Full line with timestamp, level and thread */ void printLog(const std::string &line) { - dstream< Date: Mon, 17 Oct 2011 10:47:06 +0300 Subject: [PATCH 08/40] Improve active object handling log output --- src/environment.cpp | 49 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 47743bf..5c7b295 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1343,21 +1343,26 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, bool set_changed) { assert(object); - if(object->getId() == 0) - { + if(object->getId() == 0){ u16 new_id = getFreeServerActiveObjectId(m_active_objects); + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"created new id "<setId(new_id); } + else{ + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"supplied with id "<getId()<getId(), m_active_objects) == false) { - infostream<<"ServerEnvironment::addActiveObjectRaw(): " + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " <<"id is not free ("<getId()<<")"<getId(), object); - + + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"Added id="<getId()<<"; there are now " + <getBasePosition(); std::string staticdata = object->getStaticData(); @@ -1376,6 +1386,12 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); if(block) { + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"found block for storing id="<getId() + <<" statically" + <<" (set_changed="<<(set_changed?"true":"false")<<")" + <m_static_objects.m_active.insert(object->getId(), s_obj); object->m_static_exists = true; object->m_static_block = blockpos; @@ -1384,8 +1400,8 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, block->setChangedFlag(); } else{ - infostream<<"ServerEnv: Could not find a block for " - <<"storing newly added static active object"<getId(); @@ -1460,6 +1476,10 @@ void ServerEnvironment::activateObjects(MapBlock *block) // Ignore if no stored objects (to not set changed flag) if(block->m_static_objects.m_stored.size() == 0) return; + verbosestream<<"ServerEnvironment::activateObjects(): " + <<"activating objects of block "<getPos()) + <<" ("<m_static_objects.m_stored.size() + <<" objects)"< new_stored; @@ -1536,6 +1556,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(m_active_blocks.contains(blockpos_o)) continue; + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"deactivating object id="<m_static_block = block->getPos(); } else{ - infostream<<"ServerEnv: Could not find or generate " + errorstream<<"ServerEnv: Could not find or generate " <<"a block for storing static object"<m_static_exists = false; continue; @@ -1593,12 +1617,17 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // If known by some client, don't delete. if(obj->m_known_by_count > 0 && force_delete == false) { + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"object id="<m_pending_deactivation = true; continue; } - /*infostream<<"Server: Stored static data. Deleting object." - < Date: Mon, 17 Oct 2011 11:45:06 +0300 Subject: [PATCH 09/40] Remove very floody log message of MobV2SAO --- src/content_sao.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 61a2df2..f226371 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1264,8 +1264,6 @@ void MobV2SAO::step(float dtime, bool send_recommended) m_base_position = pos_f; if((pos_f - next_pos_f).getLength() < 0.1 || arrived){ - verbosestream<<"Mob id="< Date: Mon, 17 Oct 2011 11:52:38 +0300 Subject: [PATCH 10/40] Workaround for blocks having a huge amount of active objects; add log messages related to active objects for investigation --- src/environment.cpp | 88 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 5c7b295..25e65d2 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1345,8 +1345,6 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, assert(object); if(object->getId() == 0){ u16 new_id = getFreeServerActiveObjectId(m_active_objects); - verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"created new id "<getBlockNoCreateNoEx(blockpos); if(block) { - verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"found block for storing id="<getId() - <<" statically" - <<" (set_changed="<<(set_changed?"true":"false")<<")" - <m_static_objects.m_active.insert(object->getId(), s_obj); object->m_static_exists = true; object->m_static_block = blockpos; @@ -1400,8 +1392,9 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, block->setChangedFlag(); } else{ - errorstream<<"ServerEnv: Could not find a block for " - <<"storing newly added active object statically"<getId(); @@ -1466,6 +1459,40 @@ void ServerEnvironment::removeRemovedObjects() } } +static void print_hexdump(std::ostream &o, const std::string &data) +{ + const int linelength = 16; + for(int l=0; ; l++){ + int i0 = linelength * l; + bool at_end = false; + int thislinelength = linelength; + if(i0 + thislinelength > (int)data.size()){ + thislinelength = data.size() - i0; + at_end = true; + } + for(int di=0; di= 32) + o<getPos()) <<" ("<m_static_objects.m_stored.size() <<" objects)"<m_static_objects.m_stored.size() >= 50); + if(large_amount){ + errorstream<<"suspiciously large amount of objects detected: " + <m_static_objects.m_stored.size()<<" in " + <getPos()) + <<"; not activating."< new_stored; @@ -1497,9 +1532,18 @@ void ServerEnvironment::activateObjects(MapBlock *block) // If couldn't create object, store static data back. if(obj==NULL) { + errorstream<<"ServerEnvironment::activateObjects(): " + <<"failed to create active object from static object " + <<"in block "<m_static_objects.insert(0, s_obj); + block->setChangedFlag(); + obj->m_static_exists = true; + obj->m_static_block = block->getPos(); + } } else{ errorstream<<"ServerEnv: Could not find or generate " - <<"a block for storing static object"<m_static_exists = false; + <<"a block for storing id="<getId() + <<" statically"< Date: Mon, 17 Oct 2011 17:06:28 +0300 Subject: [PATCH 11/40] Add some rendering statistics to profiler --- src/game.cpp | 1 + src/map.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/game.cpp b/src/game.cpp index e666e08..78962c1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1071,6 +1071,7 @@ void the_game( object_hit_delay_timer -= dtime; g_profiler->add("Elapsed time", dtime); + g_profiler->avg("FPS", 1./dtime); /* Log frametime for visualization diff --git a/src/map.cpp b/src/map.cpp index 63c6ad5..943d977 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #include "settings.h" #include "log.h" +#include "profiler.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -3760,6 +3761,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) p_nodes_max.Z / MAP_BLOCKSIZE + 1); u32 vertex_count = 0; + u32 meshbuffer_count = 0; // For limiting number of mesh updates per frame u32 mesh_update_count = 0; @@ -3909,6 +3911,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) sector_blocks_drawn++; u32 c = mesh->getMeshBufferCount(); + meshbuffer_count += c; for(u32 i=0; iavg("CM: blocks drawn on solid pass", blocks_drawn); + g_profiler->avg("CM: vertices drawn on solid pass", vertex_count); + if(blocks_drawn != 0) + g_profiler->avg("CM: solid meshbuffers per block", + (float)meshbuffer_count / (float)blocks_drawn); + } else { + g_profiler->avg("CM: blocks drawn on transparent pass", blocks_drawn); + g_profiler->avg("CM: vertices drawn on transparent pass", vertex_count); + } + m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; From 558a133044ac638dafb7b0e9452a4d7e435177bf Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 17:18:50 +0300 Subject: [PATCH 12/40] Display RTT (round trip time, ping) on client status text --- src/client.cpp | 7 +++++++ src/client.h | 2 ++ src/game.cpp | 5 +++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 75168c6..816ab39 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -2166,4 +2166,11 @@ ClientEvent Client::getClientEvent() return m_client_event_queue.pop_front(); } +float Client::getRTT(void) +{ + con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER); + if(!peer) + return 0.0; + return peer->avg_rtt; +} diff --git a/src/client.h b/src/client.h index 07ac930..52dd66c 100644 --- a/src/client.h +++ b/src/client.h @@ -301,6 +301,8 @@ public: return m_access_denied_reason; } + float getRTT(void); + private: // Virtual methods from con::PeerHandler diff --git a/src/game.cpp b/src/game.cpp index 78962c1..4d9b0c3 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2025,14 +2025,15 @@ void the_game( "(% .1f, % .1f, % .1f)" " (% .3f < btime_jitter < % .3f" ", dtime_jitter = % .1f %%" - ", v_range = %.1f)", + ", v_range = %.1f, RTT = %.3f)", player_position.X/BS, player_position.Y/BS, player_position.Z/BS, busytime_jitter1_min_sample, busytime_jitter1_max_sample, dtime_jitter1_max_fraction * 100.0, - draw_control.wanted_range + draw_control.wanted_range, + client.getRTT() ); guitext2->setText(narrow_to_wide(temptext).c_str()); From 93f4d2b3f126412c057747ecf430a4ffba4f66a3 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 18:02:26 +0300 Subject: [PATCH 13/40] Catch SendFailedException when replying back in Connection::Receive() --- src/connection.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/connection.cpp b/src/connection.cpp index 89cb7dd..0f09753 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1102,6 +1102,11 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) if(m_socket.WaitData(0) == true) continue; } + catch(SendFailedException &e) + { + derr_con<<"Receive(): SendFailedException; peer_id=" + < Date: Mon, 17 Oct 2011 19:44:28 +0300 Subject: [PATCH 14/40] Add a log message to SEnv and make it load objects if there are < 51 of them, to load blocks that were limited to 50 --- src/environment.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/environment.cpp b/src/environment.cpp index 25e65d2..16bdcf9 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -888,6 +888,16 @@ void ServerEnvironment::step(float dtime) continue; active_object_count_wider += block->m_static_objects.m_active.size(); + + if(block->m_static_objects.m_stored.size() != 0){ + errorstream<<"ServerEnvironment::step(): " + <getPos())<<" contains " + <m_static_objects.m_stored.size() + <<" stored objects; " + <<"when spawning objects, when counting active " + <<"objects in wide area. relative position: " + <<"("<getPos()) <<" ("<m_static_objects.m_stored.size() <<" objects)"<m_static_objects.m_stored.size() >= 50); + bool large_amount = (block->m_static_objects.m_stored.size() >= 51); if(large_amount){ errorstream<<"suspiciously large amount of objects detected: " <m_static_objects.m_stored.size()<<" in " From c41f1c960b10daf71ad07c32b0a54eb448d8b0f0 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 20:04:35 +0300 Subject: [PATCH 15/40] Fix minetest.conf.example a bit --- minetest.conf.example | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index c8cfbe8..a6f8b59 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -39,6 +39,8 @@ #keymap_jump = KEY_SPACE #keymap_sneak = KEY_LSHIFT #keymap_inventory = KEY_KEY_I +# Go down ladder / go down in fly mode / go fast in fast mode +#keymap_special1 = KEY_KEY_E #keymap_chat = KEY_KEY_T #keymap_rangeselect = KEY_KEY_R #keymap_freemove = KEY_KEY_K @@ -46,7 +48,6 @@ #keymap_frametime_graph = KEY_F1 #keymap_screenshot = KEY_F12 # Some (temporary) keys for debugging -#keymap_special1 = KEY_KEY_E #keymap_print_debug_stacks = KEY_KEY_P # The desired FPS @@ -58,8 +59,8 @@ #viewing_range_nodes_max = 300 #viewing_range_nodes_min = 25 # Initial window size -screenW# = 800 -screenH# = 600 +#screenW = 800 +#screenH = 600 # Address to connect to (#blank = start local server) #address = # Enable random user input, for testing From 6661d9be1ade13934b3fd62bc60b0484b204dbd6 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 20:19:37 +0300 Subject: [PATCH 16/40] Attempt to fix objects getting multiplicated by objects getting deactivated in a different block than where they were loaded and then the original block, from where the static object was removed, not getting saved at unload --- src/environment.cpp | 44 ++++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 16bdcf9..1319e37 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1399,7 +1399,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, object->m_static_block = blockpos; if(set_changed) - block->setChangedFlag(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); } else{ errorstream<<"ServerEnvironment::addActiveObjectRaw(): " @@ -1444,11 +1444,12 @@ void ServerEnvironment::removeRemovedObjects() */ if(obj->m_static_exists && obj->m_removed) { - MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + MapBlock *block = m_map->emergeBlock(obj->m_static_block); if(block) { block->m_static_objects.remove(id); - block->setChangedFlag(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + obj->m_static_exists = false; } } @@ -1556,7 +1557,6 @@ void ServerEnvironment::activateObjects(MapBlock *block) <<" type="<<(int)s_obj.type<m_static_objects.m_stored.clear(); @@ -1568,13 +1568,18 @@ void ServerEnvironment::activateObjects(MapBlock *block) StaticObject &s_obj = *i; block->m_static_objects.m_stored.push_back(s_obj); } - // Block has been modified - // NOTE: No it has not really. Save I/O here. - //block->setChangedFlag(); + /* + Note: Block hasn't really been modified here. + The objects have just been activated and moved from the stored + static list to the active static list. + As such, the block is essentially the same. + Thus, do not call block->setChangedFlag(). + Otherwise there would be a huge amount of unnecessary I/O. + */ } /* - Convert objects that are not in active blocks to static. + Convert objects that are not standing inside active blocks to static. If m_known_by_count != 0, active object is not deleted, but static data is still updated. @@ -1594,7 +1599,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // This shouldn't happen but check it if(obj == NULL) { - infostream<<"NULL object found in ServerEnvironment" + errorstream<<"NULL object found in ServerEnvironment" <m_static_exists) { - MapBlock *block = m_map->getBlockNoCreateNoEx - (obj->m_static_block); + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); if(block) { block->m_static_objects.remove(id); - oldblock = block; + block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD); + obj->m_static_exists = false; } - obj->m_static_exists = false; } // Create new static object @@ -1640,17 +1643,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // Get or generate the block MapBlock *block = m_map->emergeBlock(blockpos); - /*MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); - if(block == NULL) - { - // Block not found. Is the old block still ok? - if(oldblock) - block = oldblock; - // Load from disk or generate - else - block = m_map->emergeBlock(blockpos); - }*/ - if(block) { if(block->m_static_objects.m_stored.size() >= 50){ @@ -1661,7 +1653,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) force_delete = true; } else { block->m_static_objects.insert(0, s_obj); - block->setChangedFlag(); + block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD); obj->m_static_exists = true; obj->m_static_block = block->getPos(); } From 64c4d00693212a846208b3bdf5b5a877ebbd20cf Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 20:40:55 +0300 Subject: [PATCH 17/40] F2 toggles profiler display in client --- src/defaultsettings.cpp | 3 ++- src/game.cpp | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a3a8333..bb04311 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -36,6 +36,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_jump", "KEY_SPACE"); settings->setDefault("keymap_sneak", "KEY_LSHIFT"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); + settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); settings->setDefault("keymap_rangeselect", "KEY_KEY_R"); @@ -43,8 +44,8 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_fastmove", "KEY_KEY_J"); settings->setDefault("keymap_frametime_graph", "KEY_F1"); settings->setDefault("keymap_screenshot", "KEY_F12"); + settings->setDefault("keymap_toggle_profiler", "KEY_F2"); // Some (temporary) keys for debugging - settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("wanted_fps", "30"); diff --git a/src/game.cpp b/src/game.cpp index 4d9b0c3..92d4a2a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -872,7 +872,6 @@ void the_game( L"", core::rect(5, 5+(text_height+5)*1, 795, (5+text_height)*2), false, false); - // At the middle of the screen // Object infos are shown in this gui::IGUIStaticText *guitext_info = guienv->addStaticText( @@ -889,6 +888,15 @@ void the_game( //guitext_chat->setBackgroundColor(video::SColor(96,0,0,0)); core::list chat_lines; + // Profiler text + gui::IGUIStaticText *guitext_profiler = guienv->addStaticText( + L"", + core::rect(6, 4+(text_height+5)*3, 400, + (text_height+5)*3 + text_height*35), + false, false); + guitext_profiler->setBackgroundColor(video::SColor(80,0,0,0)); + guitext_profiler->setVisible(false); + /*GUIQuickInventory *quick_inventory = new GUIQuickInventory (guienv, NULL, v2s32(10, 70), 5, &local_inventory);*/ /*GUIQuickInventory *quick_inventory = new GUIQuickInventory @@ -942,6 +950,8 @@ void the_game( bool respawn_menu_active = false; + bool show_profiler = false; + /* Main loop */ @@ -1162,14 +1172,23 @@ void the_game( */ float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); - if(profiler_print_interval != 0) + bool print_to_log = true; + if(profiler_print_interval == 0){ + print_to_log = false; + profiler_print_interval = 5; + } + if(m_profiler_interval.step(0.030, profiler_print_interval)) { - if(m_profiler_interval.step(0.030, profiler_print_interval)) - { + if(print_to_log){ infostream<<"Profiler:"<print(infostream); - g_profiler->clear(); } + + std::ostringstream os(std::ios_base::binary); + g_profiler->print(os); + guitext_profiler->setText(narrow_to_wide(os.str()).c_str()); + + g_profiler->clear(); } /* @@ -1299,6 +1318,11 @@ void the_game( image->drop(); } } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler"))) + { + show_profiler = !show_profiler; + guitext_profiler->setVisible(show_profiler); + } // Item selection with mouse wheel { @@ -2114,7 +2138,8 @@ void the_game( guitext_chat->setRelativePosition(rect); - if(chat_lines.size() == 0) + // Don't show chat if empty or profiler is enabled + if(chat_lines.size() == 0 || show_profiler) guitext_chat->setVisible(false); else guitext_chat->setVisible(true); From 67db77b8b3e09d07c9cff665a270b6960fbf632d Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 20:41:43 +0300 Subject: [PATCH 18/40] Modify default active block range --- src/defaultsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index bb04311..2b3cff9 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -94,7 +94,7 @@ void set_default_settings(Settings *settings) settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("objectdata_interval", "0.2"); settings->setDefault("active_object_send_range_blocks", "3"); - settings->setDefault("active_block_range", "5"); + settings->setDefault("active_block_range", "3"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); From ea1fda5ebc7fe0aa276de768cacd81df365c69a3 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Mon, 17 Oct 2011 20:57:58 +0300 Subject: [PATCH 19/40] Made a scheme to get rid of the objects in the worst object flooded blocks --- src/environment.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 1319e37..174ee1e 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -887,9 +887,10 @@ void ServerEnvironment::step(float dtime) if(block==NULL) continue; active_object_count_wider += - block->m_static_objects.m_active.size(); + block->m_static_objects.m_active.size() + + block->m_static_objects.m_stored.size(); - if(block->m_static_objects.m_stored.size() != 0){ + /*if(block->m_static_objects.m_stored.size() != 0){ errorstream<<"ServerEnvironment::step(): " <getPos())<<" contains " <m_static_objects.m_stored.size() @@ -897,7 +898,7 @@ void ServerEnvironment::step(float dtime) <<"when spawning objects, when counting active " <<"objects in wide area. relative position: " <<"("<getPos()) <<" ("<m_static_objects.m_stored.size() <<" objects)"<m_static_objects.m_stored.size() >= 51); + bool large_amount = (block->m_static_objects.m_stored.size() > 49); if(large_amount){ errorstream<<"suspiciously large amount of objects detected: " <m_static_objects.m_stored.size()<<" in " <getPos()) - <<"; not activating."<m_static_objects.m_stored.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); return; } // A list for objects that couldn't be converted to static for some @@ -1645,10 +1649,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { - if(block->m_static_objects.m_stored.size() >= 50){ + if(block->m_static_objects.m_stored.size() >= 49){ errorstream<<"ServerEnv: Trying to store id="<getId() <<" statically but block "<m_static_objects.m_stored.size() + <<" (over 49) objects." <<" Forcing delete."< Date: Tue, 18 Oct 2011 00:01:50 +0300 Subject: [PATCH 20/40] Add /clearobjects --- src/environment.cpp | 86 ++++++++++++++++++ src/environment.h | 5 ++ src/map.cpp | 198 +++++++++++------------------------------- src/map.h | 4 + src/server.cpp | 5 ++ src/server.h | 1 + src/servercommand.cpp | 52 ++++++----- 7 files changed, 185 insertions(+), 166 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 174ee1e..1422718 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -647,6 +647,92 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) } } +void ServerEnvironment::clearAllObjects() +{ + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Removing all active objects"< objects_to_remove; + for(core::map::Iterator + i = m_active_objects.getIterator(); + i.atEnd()==false; i++) + { + ServerActiveObject* obj = i.getNode()->getValue(); + u16 id = i.getNode()->getKey(); + v3f objectpos = obj->getBasePosition(); + // Delete static object if block is loaded + if(obj->m_static_exists){ + MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + if(block){ + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + obj->m_static_exists = false; + } + } + // If known by some client, don't delete immediately + if(obj->m_known_by_count > 0){ + obj->m_pending_deactivation = true; + obj->m_removed = true; + continue; + } + // Delete active object + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + // Remove references from m_active_objects + for(core::list::Iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); i++) + { + m_active_objects.remove(*i); + } + + core::list loadable_blocks; + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Listing all loadable blocks"<listAllLoadableBlocks(loadable_blocks); + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Done listing all loadable blocks: " + <::Iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); i++) + { + v3s16 p = *i; + MapBlock *block = m_map->emergeBlock(p, false); + if(!block){ + errorstream<<"ServerEnvironment::clearAllObjects(): " + <<"Failed to emerge block "<m_static_objects.m_stored.size(); + u32 num_active = block->m_static_objects.m_active.size(); + if(num_stored != 0 || num_active != 0){ + block->m_static_objects.m_stored.clear(); + block->m_static_objects.m_active.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED); + num_objs_cleared += num_stored + num_active; + num_blocks_cleared++; + } + num_blocks_checked++; + + if(num_blocks_checked % report_interval == 0){ + float percent = 100.0 * (float)num_blocks_checked / + loadable_blocks.size(); + infostream<<"ServerEnvironment::clearAllObjects(): " + <<"Cleared "< MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - throw InvalidPositionException("emergeBlock(): pos. over limit"); - - v2s16 p2d(p.X, p.Z); - s16 block_y = p.Y; - /* - This will create or load a sector if not found in memory. - If block exists on disk, it will be loaded. - */ - ServerMapSector *sector; - try{ - sector = createSector(p2d); - //sector = emergeSector(p2d, changed_blocks); - } - catch(InvalidPositionException &e) - { - infostream<<"emergeBlock: createSector() failed: " - <getBlockNoCreateNoEx(block_y); - - // If not found, try loading from disk - if(block == NULL) - { - block = loadBlock(p); - } - - // Handle result - if(block == NULL) - { - does_not_exist = true; - } - else if(block->isDummy() == true) - { - does_not_exist = true; - } - else if(block->getLightingExpired()) - { - lighting_expired = true; - } - else - { - // Valid block - //infostream<<"emergeBlock(): Returning already valid block"<insertBlock(block); - } - // Done. - return block; - } - - //infostream<<"Not found on disk, generating."< making one"< light_sources; - bool black_air_left = false; - bool bottom_invalid = - block->propagateSunlight(light_sources, true, - &black_air_left); - - // If sunlight didn't reach everywhere and part of block is - // above ground, lighting has to be properly updated - //if(black_air_left && some_part_underground) - if(black_air_left) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - - if(bottom_invalid) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - } -#endif - - return block; -} -#endif - s16 ServerMap::findGroundLevel(v2s16 p2d) { #if 0 @@ -2867,6 +2721,12 @@ void ServerMap::verifyDatabase() { throw FileNotGoodException("Cannot prepare write statement"); } + d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL); + if(d != SQLITE_OK) { + infostream<<"WARNING: Database list statment failed to prepare: "<= 0) + return i % mod; + return mod - ((-i) % mod); +} + +v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i) +{ + s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - x) / 4096; + s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - y) / 4096; + s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048); + return v3s16(x,y,z); +} + +void ServerMap::listAllLoadableBlocks(core::list &dst) +{ + if(loadFromFolders()){ + errorstream<<"Map::listAllLoadableBlocks(): Result will be missing " + <<"all blocks that are stored in flat files"<privs & PRIV_SERVER) ==0) + { + os<player->getName() + <<" clears all objects"<player->getName()); + msg += L")"; + ctx->server->notifyPlayers(msg); + } + + ctx->env->clearAllObjects(); + + actionstream<<"object clearing done"<flags |= SEND_TO_OTHERS; +} + std::wstring processServerCommand(ServerCommandContext *ctx) { @@ -302,45 +331,28 @@ std::wstring processServerCommand(ServerCommandContext *ctx) os<parms[0] == L"status") - { cmd_status(os, ctx); - } else if(ctx->parms[0] == L"privs") - { cmd_privs(os, ctx); - } else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke") - { cmd_grantrevoke(os, ctx); - } else if(ctx->parms[0] == L"time") - { cmd_time(os, ctx); - } else if(ctx->parms[0] == L"shutdown") - { cmd_shutdown(os, ctx); - } else if(ctx->parms[0] == L"setting") - { cmd_setting(os, ctx); - } else if(ctx->parms[0] == L"teleport") - { cmd_teleport(os, ctx); - } else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban") - { cmd_banunban(os, ctx); - } else if(ctx->parms[0] == L"me") - { cmd_me(os, ctx); - } + else if(ctx->parms[0] == L"clearobjects") + cmd_clearobjects(os, ctx); else - { os<parms[0]; - } + return os.str(); } From 6ce0c61dc0001f5eba265bbf3463ae9b38d310b9 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 00:41:02 +0300 Subject: [PATCH 21/40] Fix possible NULL pointer access in MobV2CAO --- src/content_cao.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index b17c281..67594eb 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -993,6 +993,8 @@ void MobV2CAO::updateNodePos() void MobV2CAO::step(float dtime, ClientEnvironment *env) { scene::MyBillboardSceneNode *bill = m_node; + if(!bill) + return; pos_translator.translate(dtime); From fe338745a3bf008c337c24f080c83c7c557c08ed Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 00:41:17 +0300 Subject: [PATCH 22/40] Make active_block_range default to 2 --- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index a6f8b59..6f817d3 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -140,7 +140,7 @@ # Player and object positions are sent at intervals specified by this #objectdata_interval = 0.2 #active_object_send_range_blocks = 3 -#active_block_range = 5 +#active_block_range = 2 #max_simultaneous_block_sends_per_client = 2 #max_simultaneous_block_sends_server_total = 8 #max_block_send_distance = 8 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 2b3cff9..b05d060 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -94,7 +94,7 @@ void set_default_settings(Settings *settings) settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("objectdata_interval", "0.2"); settings->setDefault("active_object_send_range_blocks", "3"); - settings->setDefault("active_block_range", "3"); + settings->setDefault("active_block_range", "2"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); From 22b07bdb30f602d9c469d97b138cf09ee131de62 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 02:58:15 +0300 Subject: [PATCH 23/40] Fix object duplication bug --- src/environment.cpp | 59 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 1422718..80e9d5c 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1695,6 +1695,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + // If pending deactivation, let removeRemovedObjects() do it + if(obj->m_pending_deactivation) + continue; + u16 id = i.getNode()->getKey(); v3f objectpos = obj->getBasePosition(); @@ -1709,10 +1713,42 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) <<"deactivating object id="<m_known_by_count > 0 && !force_delete); + /* Update the static data */ + // Create new static object + std::string staticdata_new = obj->getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + + bool stays_in_same_block = false; + bool data_changed = true; + + if(obj->m_static_exists){ + if(obj->m_static_block == blockpos_o) + stays_in_same_block = true; + + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + + core::map::Node *n = + block->m_static_objects.m_active.find(id); + if(n){ + StaticObject static_old = n->getValue(); + + if(static_old.data == staticdata_new && + (static_old.pos - objectpos).getLength() < 2*BS) + data_changed = false; + } else { + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"id="<m_static_block)<m_static_exists) { @@ -1720,14 +1756,13 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { block->m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD); obj->m_static_exists = false; + // Only mark block as modified if data changed considerably + if(!stays_in_same_block || data_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED); } } - // Create new static object - std::string staticdata = obj->getStaticData(); - StaticObject s_obj(obj->getType(), objectpos, staticdata); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); // Get or generate the block @@ -1744,8 +1779,13 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) <<" Forcing delete."<m_static_objects.insert(0, s_obj); - block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD); + u16 new_id = pending_delete ? id : 0; + block->m_static_objects.insert(new_id, s_obj); + + // Only mark block as modified if data changed considerably + if(!stays_in_same_block || data_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED); + obj->m_static_exists = true; obj->m_static_block = block->getPos(); } @@ -1758,12 +1798,11 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } /* - Delete active object if not known by some client, - else set pending deactivation + If known by some client, set pending deactivation. + Otherwise delete it immediately. */ - // If known by some client, don't delete. - if(obj->m_known_by_count > 0 && force_delete == false) + if(pending_delete) { verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Tue, 18 Oct 2011 03:42:23 +0300 Subject: [PATCH 24/40] Better handling of SendFailedException in Connection --- src/connection.cpp | 9 ++++++--- src/socket.cpp | 2 +- src/socket.h | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/connection.cpp b/src/connection.cpp index 0f09753..623994c 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1104,8 +1104,6 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) } catch(SendFailedException &e) { - derr_con<<"Receive(): SendFailedException; peer_id=" - <>24; diff --git a/src/socket.h b/src/socket.h index f24947c..74b5fb6 100644 --- a/src/socket.h +++ b/src/socket.h @@ -97,7 +97,7 @@ public: void setPort(unsigned short port); void print(std::ostream *s) const; void print() const; - std::string serializeString(); + std::string serializeString() const; private: unsigned int m_address; unsigned short m_port; From eae2d35ca568ccb652169f83e6a995db273fba94 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 10:36:52 +0300 Subject: [PATCH 25/40] Fix client profiler print interval --- src/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index 92d4a2a..645d736 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1177,7 +1177,7 @@ void the_game( print_to_log = false; profiler_print_interval = 5; } - if(m_profiler_interval.step(0.030, profiler_print_interval)) + if(m_profiler_interval.step(dtime, profiler_print_interval)) { if(print_to_log){ infostream<<"Profiler:"< Date: Tue, 18 Oct 2011 11:31:23 +0300 Subject: [PATCH 26/40] Automate texture listing for texture atlas making --- src/content_mapnode.cpp | 1 + src/main.cpp | 4 +- src/mapnode.cpp | 2 + src/mapnode.h | 8 +++ src/tile.cpp | 110 ++++++++++++++++++++++++---------------- 5 files changed, 81 insertions(+), 44 deletions(-) diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index af5e989..139c46f 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -117,6 +117,7 @@ void content_mapnode_init() f->setInventoryTextureCube("stone.png", "stone.png", "stone.png"); f->param_type = CPT_MINERAL; f->is_ground_content = true; + f->often_contains_mineral = true; f->dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1"; setStoneLikeDiggingProperties(f->digging_properties, 1.0); if(invisible_stone) diff --git a/src/main.cpp b/src/main.cpp index be98367..df1347f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1278,6 +1278,9 @@ int main(int argc, char *argv[]) // Initial call with g_texturesource not set. init_mapnode(); + // Must be called before g_texturesource is created + // (for texture atlas making) + init_mineral(); /* Run unit tests @@ -1475,7 +1478,6 @@ int main(int argc, char *argv[]) */ init_mapnode(); // Second call with g_texturesource set - init_mineral(); /* GUI stuff diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 170500f..484fcbe 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -42,6 +42,8 @@ ContentFeatures::~ContentFeatures() #ifndef SERVER void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha) { + used_texturenames[name] = true; + if(g_texturesource) { tiles[i].texture = g_texturesource->getTexture(name); diff --git a/src/mapnode.h b/src/mapnode.h index 5db3379..61f172e 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -103,6 +103,9 @@ class NodeMetadata; struct ContentFeatures { #ifndef SERVER + // List of all block textures that have been used (value is dummy) + core::map used_texturenames; + /* 0: up 1: down @@ -151,6 +154,10 @@ struct ContentFeatures // If true, node is equivalent to air. Torches are, air is. Water is not. // Is used for example to check whether a mud block can have grass on. bool air_equivalent; + // Whether this content type often contains mineral. + // Used for texture atlas creation. + // Currently only enabled for CONTENT_STONE. + bool often_contains_mineral; // Inventory item string as which the node appears in inventory when dug. // Mineral overrides this. @@ -207,6 +214,7 @@ struct ContentFeatures liquid_type = LIQUID_NONE; wall_mounted = false; air_equivalent = false; + often_contains_mineral = false; dug_item = ""; initial_metadata = NULL; liquid_alternative_flowing = CONTENT_IGNORE; diff --git a/src/tile.cpp b/src/tile.cpp index e274d2d..366f2df 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include #include "log.h" +#include "mapnode.h" // For texture atlas making +#include "mineral.h" // For texture atlas making /* A cache from texture name to texture path @@ -507,52 +509,62 @@ void TextureSource::buildMainAtlas() } /* - A list of stuff to include in the texture atlas. - - It is a single-dimensional texture atlas due to the need to tile - textures. - - It should contain as much of the stuff shown in game as possible, - to minimize texture changes. - - It fills up quickly, so do not add anything that isn't contained - in most MapBlocks. E.g. mese isn't suitable but stone is. + Grab list of stuff to include in the texture atlas from the + main content features */ - core::array sourcelist; + core::map sourcelist; - sourcelist.push_back("stone.png"); - sourcelist.push_back("mud.png"); - sourcelist.push_back("sand.png"); - sourcelist.push_back("grass.png"); - sourcelist.push_back("grass_footsteps.png"); - sourcelist.push_back("tree.png"); - sourcelist.push_back("tree_top.png"); - sourcelist.push_back("water.png"); - sourcelist.push_back("leaves.png"); - sourcelist.push_back("glass.png"); - sourcelist.push_back("mud.png^grass_side.png"); - sourcelist.push_back("cobble.png"); - sourcelist.push_back("mossycobble.png"); - sourcelist.push_back("gravel.png"); - sourcelist.push_back("jungletree.png"); - - sourcelist.push_back("stone.png^mineral_coal.png"); - sourcelist.push_back("stone.png^mineral_iron.png"); + for(u16 j=0; j::Iterator + i = f->used_texturenames.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + sourcelist[name] = true; + + if(f->often_contains_mineral){ + for(int k=1; k::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) + { + std::string name = i.getNode()->getKey(); + infostream<<"\""< pos_in_atlas(0,0); - pos_in_atlas.Y += padding; + pos_in_atlas.Y = padding; - for(u32 i=0; i::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); /*video::IImage *img = driver->createImageFromFile( getTexturePath(name.c_str()).c_str()); @@ -586,20 +598,26 @@ void TextureSource::buildMainAtlas() continue; } - // Stop making atlas if atlas is full + // Wrap columns and stop making atlas if atlas is full if(pos_in_atlas.Y + dim.Height > atlas_dim.Height) { - infostream<<"TextureSource::buildMainAtlas(): " - <<"Atlas is full, not adding more textures." - < (s32)atlas_dim.Width - 256 - padding){ + errorstream<<"TextureSource::buildMainAtlas(): " + <<"Atlas is full, not adding more textures." + < 16) // Limit to 16 (more gives no benefit) + xwise_tiling = 16; for(u32 j=0; jgetPixel(x, src_y); atlas_img->setPixel(x,dst_y,c); } @@ -668,9 +686,11 @@ void TextureSource::buildMainAtlas() /* Second pass: set texture pointer in generated AtlasPointers */ - for(u32 i=0; i::Iterator + i = sourcelist.getIterator(); + i.atEnd() == false; i++) { - std::string name = sourcelist[i]; + std::string name = i.getNode()->getKey(); if(m_name_to_id.find(name) == NULL) continue; u32 id = m_name_to_id[name]; @@ -681,8 +701,12 @@ void TextureSource::buildMainAtlas() /* Write image to file so that it can be inspected */ - /*driver->writeImageToFile(atlas_img, - getTexturePath("main_atlas.png").c_str());*/ + /*std::string atlaspath = porting::path_userdata + + DIR_DELIM + "generated_texture_atlas.png"; + infostream<<"Removing and writing texture atlas for inspection to " + <writeImageToFile(atlas_img, atlaspath.c_str());*/ } video::IImage* generate_image_from_scratch(std::string name, From 554f7f120c4bc99a2c9c944b951662b95a03a9d4 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 13:56:35 +0300 Subject: [PATCH 27/40] Improve rendering and fix tiling in mesh generation --- src/clouds.cpp | 4 + src/content_mapblock.cpp | 225 +++++++++++++++++++-------------------- src/content_mapnode.cpp | 14 +++ src/mapblock_mesh.cpp | 36 ++++++- src/tile.h | 4 +- 5 files changed, 167 insertions(+), 116 deletions(-) diff --git a/src/clouds.cpp b/src/clouds.cpp index d754cc1..8981b42 100644 --- a/src/clouds.cpp +++ b/src/clouds.cpp @@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "constants.h" #include "debug.h" +#include "profiler.h" +#include "main.h" // For g_profiler Clouds::Clouds( scene::ISceneNode* parent, @@ -76,6 +78,8 @@ void Clouds::render() if(SceneManager->getSceneNodeRenderPass() != scene::ESNRP_SOLID) return; + ScopeProfiler sp(g_profiler, "Rendering of clouds, avg", SPT_AVG); + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setMaterial(m_material); diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 0c96c56..ad4601d 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -143,7 +143,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // New-style leaves material video::SMaterial material_leaves1; material_leaves1.setFlag(video::EMF_LIGHTING, false); - //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false); material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; @@ -229,25 +228,54 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 p(x,y,z); MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p); - + /* Add torches to mesh */ if(n.getContent() == CONTENT_TORCH) { + v3s16 dir = unpackDir(n.param2); + + const char *texturename = "torch.png"; + if(dir == v3s16(0,-1,0)){ + texturename = "torch_on_floor.png"; + } else if(dir == v3s16(0,1,0)){ + texturename = "torch_on_ceiling.png"; + // For backwards compatibility + } else if(dir == v3s16(0,0,0)){ + texturename = "torch_on_floor.png"; + } else { + texturename = "torch.png"; + } + + AtlasPointer ap = g_texturesource->getTexture(texturename); + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material.setTexture(0, ap.atlas); + video::SColor c(255,255,255,255); // Wall at X+ of node video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,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), + video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, + ap.x0(), ap.y0()), }; - v3s16 dir = unpackDir(n.param2); - for(s32 i=0; i<4; i++) { if(dir == v3s16(1,0,0)) @@ -266,29 +294,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - if(dir == v3s16(0,-1,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_floor.png")); - else if(dir == v3s16(0,1,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_ceiling.png")); - // For backwards compatibility - else if(dir == v3s16(0,0,0)) - material.setTexture(0, - g_texturesource->getTextureRaw("torch_on_floor.png")); - else - material.setTexture(0, - g_texturesource->getTextureRaw("torch.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); @@ -298,6 +303,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, */ else if(n.getContent() == CONTENT_SIGN_WALL) { + AtlasPointer ap = g_texturesource->getTexture("sign_wall.png"); + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material.setTexture(0, ap.atlas); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c = MapBlock_LightColor(255, l); @@ -305,10 +322,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Wall at X+ of node video::S3DVertex vertices[4] = { - video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y0()), }; v3s16 dir = unpackDir(n.param2); @@ -331,19 +352,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - - material.setTexture(0, - g_texturesource->getTextureRaw("sign_wall.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material, vertices, 4, indices, 6); @@ -525,10 +533,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -610,10 +614,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -668,10 +668,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, pa_liquid1.x0(), pa_liquid1.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, @@ -705,10 +701,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, pa_leaves1.x0(), pa_leaves1.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, @@ -927,10 +919,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, @@ -1034,13 +1022,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y1()), + pa_junglegrass.x0(), pa_junglegrass.y1()), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y1()), + pa_junglegrass.x1(), pa_junglegrass.y1()), video::S3DVertex(BS/2,BS/1,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y0()), + pa_junglegrass.x1(), pa_junglegrass.y0()), video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y0()), + pa_junglegrass.x0(), pa_junglegrass.y0()), }; if(j == 0) @@ -1077,9 +1065,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } else if(n.getContent() == CONTENT_RAIL) { - u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); - video::SColor c = MapBlock_LightColor(255, l); - bool is_rail_x [] = { false, false }; /* x-1, x+1 */ bool is_rail_z [] = { false, false }; /* z-1, z+1 */ @@ -1097,43 +1082,50 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(n_plus_z.getContent() == CONTENT_RAIL) is_rail_z[1] = true; - float d = (float)BS/16; - video::S3DVertex vertices[4] = + int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; + + // Assign textures + const char *texturename = "rail.png"; + if(adjacencies < 2) + texturename = "rail.png"; + else if(adjacencies == 2) { - video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, - 0, 1), - video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, - 1, 1), - video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, - 1, 0), - video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, - 0, 0), - }; + if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) + texturename = "rail.png"; + else + texturename = "rail_curved.png"; + } + else if(adjacencies == 3) + texturename = "rail_t_junction.png"; + else if(adjacencies == 4) + texturename = "rail_crossing.png"; + + AtlasPointer ap = g_texturesource->getTexture(texturename); video::SMaterial material_rail; material_rail.setFlag(video::EMF_LIGHTING, false); - material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false); + material_rail.setFlag(video::EMF_BACK_FACE_CULLING, true); material_rail.setFlag(video::EMF_BILINEAR_FILTER, false); material_rail.setFlag(video::EMF_FOG_ENABLE, true); material_rail.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material_rail.setTexture(0, ap.atlas); - int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1]; + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); + video::SColor c = MapBlock_LightColor(255, l); - // Assign textures - if(adjacencies < 2) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); - else if(adjacencies == 2) + float d = (float)BS/16; + video::S3DVertex vertices[4] = { - if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1])) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png")); - else - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png")); - } - else if(adjacencies == 3) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png")); - else if(adjacencies == 4) - material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png")); + video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c, + ap.x0(), ap.y0()), + }; // Rotate textures int angle = 0; @@ -1180,6 +1172,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data, collector.append(material_rail, vertices, 4, indices, 6); } else if (n.getContent() == CONTENT_LADDER) { + AtlasPointer ap = g_texturesource->getTexture("ladder.png"); + + // Set material + video::SMaterial material_ladder; + material_ladder.setFlag(video::EMF_LIGHTING, false); + material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, true); + material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false); + material_ladder.setFlag(video::EMF_FOG_ENABLE, true); + material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material_ladder.setTexture(0, ap.atlas); + u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio)); video::SColor c(255,l,l,l); @@ -1188,10 +1191,14 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Assume wall is at X+ video::S3DVertex vertices[4] = { - video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y1()), + video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y1()), + video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, + ap.x1(), ap.y0()), + video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, + ap.x0(), ap.y0()), }; v3s16 dir = unpackDir(n.param2); @@ -1214,14 +1221,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); } - video::SMaterial material_ladder; - material_ladder.setFlag(video::EMF_LIGHTING, false); - material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false); - material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false); - material_ladder.setFlag(video::EMF_FOG_ENABLE, true); - material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - material_ladder.setTexture(0, g_texturesource->getTextureRaw("ladder.png")); - u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector collector.append(material_ladder, vertices, 4, indices, 6); diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 139c46f..ec0a7df 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -220,6 +220,7 @@ void content_mapnode_init() i = CONTENT_JUNGLEGRASS; f = &content_features(i); f->setInventoryTexture("junglegrass.png"); + f->used_texturenames["junglegrass.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; //f->is_ground_content = true; @@ -265,6 +266,7 @@ void content_mapnode_init() i = CONTENT_PAPYRUS; f = &content_features(i); f->setInventoryTexture("papyrus.png"); + f->used_texturenames["papyrus.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -307,11 +309,13 @@ void content_mapnode_init() f->solidness = 0; // drawn separately, makes no faces f->air_equivalent = true; // grass grows underneath f->setInventoryTexture("fence.png"); + f->used_texturenames["fence.png"] = true; setWoodLikeDiggingProperties(f->digging_properties, 0.75); i = CONTENT_RAIL; f = &content_features(i); f->setInventoryTexture("rail.png"); + f->used_texturenames["rail.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -324,6 +328,7 @@ void content_mapnode_init() i = CONTENT_LADDER; f = &content_features(i); f->setInventoryTexture("ladder.png"); + f->used_texturenames["ladder.png"] = true; f->light_propagates = true; f->param_type = CPT_LIGHT; f->is_ground_content = true; @@ -466,6 +471,7 @@ void content_mapnode_init() i = CONTENT_LAVA; f = &content_features(i); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); + f->used_texturenames["lava.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = false; f->light_source = LIGHT_MAX-1; @@ -502,6 +508,7 @@ void content_mapnode_init() i = CONTENT_LAVASOURCE; f = &content_features(i); f->setInventoryTextureCube("lava.png", "lava.png", "lava.png"); + f->used_texturenames["ladder.png"] = true; if(new_style_water) { f->solidness = 0; // drawn separately, makes no faces @@ -555,6 +562,10 @@ void content_mapnode_init() i = CONTENT_TORCH; f = &content_features(i); f->setInventoryTexture("torch_on_floor.png"); + f->used_texturenames["torch_on_floor.png"] = true; + f->used_texturenames["torch_on_ceiling.png"] = true; + f->used_texturenames["torch_on_floor.png"] = true; + f->used_texturenames["torch.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; @@ -569,6 +580,7 @@ void content_mapnode_init() i = CONTENT_SIGN_WALL; f = &content_features(i); f->setInventoryTexture("sign_wall.png"); + f->used_texturenames["sign_wall.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; @@ -671,6 +683,7 @@ void content_mapnode_init() f->param_type = CPT_LIGHT; f->setAllTextures("sapling.png"); f->setInventoryTexture("sapling.png"); + f->used_texturenames["sapling.png"] = true; f->dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; f->light_propagates = true; f->air_equivalent = false; @@ -681,6 +694,7 @@ void content_mapnode_init() i = CONTENT_APPLE; f = &content_features(i); f->setInventoryTexture("apple.png"); + f->used_texturenames["apple.png"] = true; f->param_type = CPT_LIGHT; f->light_propagates = true; f->sunlight_propagates = true; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 2bee572..7ee4998 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_settings and g_texturesource #include "content_mapblock.h" #include "settings.h" +#include "profiler.h" void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) { @@ -527,7 +528,7 @@ void updateFastFaceRow( next_tile); if(next_makes_face == makes_face - && next_p_corrected == p_corrected + && next_p_corrected == p_corrected + translate_dir && next_face_dir_corrected == face_dir_corrected && next_lights[0] == lights[0] && next_lights[1] == lights[1] @@ -537,6 +538,29 @@ void updateFastFaceRow( { next_is_different = false; } + else{ + /*if(makes_face){ + g_profiler->add("Meshgen: diff: next_makes_face != makes_face", + next_makes_face != makes_face ? 1 : 0); + g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir", + (next_p_corrected != p_corrected + translate_dir) ? 1 : 0); + g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr", + next_face_dir_corrected != face_dir_corrected ? 1 : 0); + g_profiler->add("Meshgen: diff: next_lights[] != lights[]", + (next_lights[0] != lights[0] || + next_lights[0] != lights[0] || + next_lights[0] != lights[0] || + next_lights[0] != lights[0]) ? 1 : 0); + g_profiler->add("Meshgen: diff: !(next_tile == tile)", + !(next_tile == tile) ? 1 : 0); + }*/ + } + /*g_profiler->add("Meshgen: Total faces checked", 1); + if(makes_face) + g_profiler->add("Meshgen: Total makes_face checked", 1);*/ + } else { + /*if(makes_face) + g_profiler->add("Meshgen: diff: last position", 1);*/ } continuous_tiles_count++; @@ -568,6 +592,8 @@ void updateFastFaceRow( v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z); // Center point of face (kind of) v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f; + if(continuous_tiles_count != 1) + sp += translate_dir_f; v3f scale(1,1,1); if(translate_dir.X != 0) @@ -586,6 +612,11 @@ void updateFastFaceRow( makeFastFace(tile, lights[0], lights[1], lights[2], lights[3], sp, face_dir_corrected, scale, posRelative_f, dest); + + g_profiler->avg("Meshgen: faces drawn by tiling", 0); + for(int i=1; iavg("Meshgen: faces drawn by tiling", 1); + } } continuous_tiles_count = 0; @@ -707,10 +738,13 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data) video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF); //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE); + material.MaterialType + = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; for(u32 i=0; i Date: Tue, 18 Oct 2011 17:20:54 +0300 Subject: [PATCH 28/40] ...Make the the server buildable again after the last commit --- src/mapnode.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mapnode.h b/src/mapnode.h index 61f172e..fb72443 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -103,9 +103,6 @@ class NodeMetadata; struct ContentFeatures { #ifndef SERVER - // List of all block textures that have been used (value is dummy) - core::map used_texturenames; - /* 0: up 1: down @@ -127,6 +124,10 @@ struct ContentFeatures AtlasPointer *special_atlas; #endif + // List of all block textures that have been used (value is dummy) + // Exists on server too for cleaner code in content_mapnode.cpp + core::map used_texturenames; + // Type of MapNode::param1 ContentParamType param_type; // True for all ground-like things like stone and mud, false for eg. trees From 8ead29a302e5bad1f67d2e83c4999c9164c6c03d Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 18:32:35 +0300 Subject: [PATCH 29/40] set backface culling off again for torches, ladders, rails and signs --- src/content_mapblock.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index ad4601d..5e1bac2 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -253,7 +253,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Set material video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -308,7 +308,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Set material video::SMaterial material; material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_FOG_ENABLE, true); material.MaterialType @@ -1104,7 +1104,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::SMaterial material_rail; material_rail.setFlag(video::EMF_LIGHTING, false); - material_rail.setFlag(video::EMF_BACK_FACE_CULLING, true); + material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false); material_rail.setFlag(video::EMF_BILINEAR_FILTER, false); material_rail.setFlag(video::EMF_FOG_ENABLE, true); material_rail.MaterialType @@ -1177,7 +1177,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Set material video::SMaterial material_ladder; material_ladder.setFlag(video::EMF_LIGHTING, false); - material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, true); + material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false); material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false); material_ladder.setFlag(video::EMF_FOG_ENABLE, true); material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; From 4e1055543c01af8f17f1ab2e742922575170251e Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 19:18:01 +0300 Subject: [PATCH 30/40] Tune map rendering and related diagnostics --- src/camera.cpp | 4 ++- src/game.cpp | 6 ++-- src/map.cpp | 73 +++++++++++++++++++++++++++++++++++++------------ src/utility.cpp | 2 +- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 634a7cc..ecb5a17 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -364,7 +364,9 @@ void Camera::updateViewingRange(f32 frametime_in) //dstream<<"wanted_frametime_change="<setFog( diff --git a/src/map.cpp b/src/map.cpp index c7f635f..febc40d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3657,14 +3657,15 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d; // Take a fair amount as we will be dropping more out later + // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( p_nodes_min.X / MAP_BLOCKSIZE - 2, p_nodes_min.Y / MAP_BLOCKSIZE - 2, p_nodes_min.Z / MAP_BLOCKSIZE - 2); v3s16 p_blocks_max( - p_nodes_max.X / MAP_BLOCKSIZE + 1, - p_nodes_max.Y / MAP_BLOCKSIZE + 1, - p_nodes_max.Z / MAP_BLOCKSIZE + 1); + p_nodes_max.X / MAP_BLOCKSIZE + 0, + p_nodes_max.Y / MAP_BLOCKSIZE + 0, + p_nodes_max.Z / MAP_BLOCKSIZE + 0); u32 vertex_count = 0; u32 meshbuffer_count = 0; @@ -3672,8 +3673,19 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // For limiting number of mesh updates per frame u32 mesh_update_count = 0; + // Number of blocks in rendering range + u32 blocks_in_range = 0; + // Number of blocks in rendering range but don't have a mesh + u32 blocks_in_range_without_mesh = 0; + // Blocks that had mesh that would have been drawn according to + // rendering range (if max blocks limit didn't kick in) u32 blocks_would_have_drawn = 0; + // Blocks that were drawn and had a mesh u32 blocks_drawn = 0; + // Blocks which had a corresponding meshbuffer for this pass + u32 blocks_had_pass_meshbuf = 0; + // Blocks from which stuff was actually drawn + u32 blocks_without_stuff = 0; int timecheck_counter = 0; core::map::Iterator si; @@ -3746,6 +3758,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ + + blocks_in_range++; #if 1 /* @@ -3764,8 +3778,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Mesh has not been expired and there is no mesh: // block has no content - if(block->mesh == NULL && mesh_expired == false) + if(block->mesh == NULL && mesh_expired == false){ + blocks_in_range_without_mesh++; continue; + } } f32 faraway = BS*50; @@ -3803,9 +3819,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) JMutexAutoLock lock(block->mesh_mutex); scene::SMesh *mesh = block->mesh; - - if(mesh == NULL) + + if(mesh == NULL){ + blocks_in_range_without_mesh++; continue; + } blocks_would_have_drawn++; if(blocks_drawn >= m_control.wanted_max_blocks @@ -3817,8 +3835,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) sector_blocks_drawn++; u32 c = mesh->getMeshBufferCount(); - meshbuffer_count += c; - + bool stuff_actually_drawn = false; for(u32 i=0; igetMeshBuffer(i); @@ -3829,16 +3846,25 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { + if(buf->getVertexCount() == 0) + errorstream<<"Block ["<setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); + meshbuffer_count++; + stuff_actually_drawn = true; } } + if(stuff_actually_drawn) + blocks_had_pass_meshbuf++; + else + blocks_without_stuff++; } } // foreach sectorblocks @@ -3848,17 +3874,30 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } + std::string prefix = "CM: "; + + // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID){ - g_profiler->avg("CM: blocks drawn on solid pass", blocks_drawn); - g_profiler->avg("CM: vertices drawn on solid pass", vertex_count); - if(blocks_drawn != 0) - g_profiler->avg("CM: solid meshbuffers per block", - (float)meshbuffer_count / (float)blocks_drawn); - } else { - g_profiler->avg("CM: blocks drawn on transparent pass", blocks_drawn); - g_profiler->avg("CM: vertices drawn on transparent pass", vertex_count); + g_profiler->avg(prefix+"blocks in range", blocks_in_range); + if(blocks_in_range != 0) + g_profiler->avg(prefix+"blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh/blocks_in_range); + g_profiler->avg(prefix+"blocks drawn", blocks_drawn); } + if(pass == scene::ESNRP_SOLID) + prefix = "CM: solid: "; + else + prefix = "CM: transparent: "; + + g_profiler->avg(prefix+"vertices drawn", vertex_count); + if(blocks_had_pass_meshbuf != 0) + g_profiler->avg(prefix+"meshbuffers per block", + (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); + if(blocks_drawn != 0) + g_profiler->avg(prefix+"empty blocks (frac)", + (float)blocks_without_stuff / blocks_drawn); + m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; diff --git a/src/utility.cpp b/src/utility.cpp index 6ce67cb..0139281 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -236,7 +236,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // If block is (nearly) touching the camera, don't // bother validating further (that is, render it anyway) - if(d > block_max_radius * 1.5) + if(d > block_max_radius) { // Cosine of the angle between the camera direction // and the block direction (camera_dir is an unit vector) From 28f2fdb6dec0c54ce9e32df79a73ef19aee03fd5 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 19:53:34 +0300 Subject: [PATCH 31/40] Fix possible NULL dereference in MobV2CAO::step --- src/content_cao.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 67594eb..6cb2cee 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1000,6 +1000,8 @@ void MobV2CAO::step(float dtime, ClientEnvironment *env) if(m_sprite_type == "humanoid_1"){ scene::ICameraSceneNode* camera = m_node->getSceneManager()->getActiveCamera(); + if(!camera) + return; v3f cam_to_mob = m_node->getAbsolutePosition() - camera->getAbsolutePosition(); cam_to_mob.normalize(); int col = 0; From 9b907dd65a2c045d10605894fdaea504200e2be7 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 21:08:31 +0300 Subject: [PATCH 32/40] Try to tune fog to work well on high-end machines also --- src/camera.cpp | 4 ++-- src/defaultsettings.cpp | 1 + src/game.cpp | 11 ++++++++--- src/map.cpp | 12 ++++++------ 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index ecb5a17..a323367 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -342,7 +342,7 @@ void Camera::updateViewingRange(f32 frametime_in) <setDefault("keymap_frametime_graph", "KEY_F1"); settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_toggle_profiler", "KEY_F2"); + settings->setDefault("keymap_toggle_force_fog_off", "KEY_F3"); // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); diff --git a/src/game.cpp b/src/game.cpp index e3380ac..c67660e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -952,6 +952,8 @@ void the_game( bool show_profiler = false; + bool force_fog_off = false; + /* Main loop */ @@ -1323,6 +1325,10 @@ void the_game( show_profiler = !show_profiler; guitext_profiler->setVisible(show_profiler); } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off"))) + { + force_fog_off = !force_fog_off; + } // Item selection with mouse wheel { @@ -1971,7 +1977,7 @@ void the_game( Fog */ - if(g_settings->getBool("enable_fog") == true) + if(g_settings->getBool("enable_fog") == true && !force_fog_off) { f32 range; if(farmesh) @@ -1981,12 +1987,11 @@ void the_game( else { range = draw_control.wanted_range*BS + MAP_BLOCKSIZE*BS*1.5; + range *= 0.9; if(draw_control.range_all) range = 100000*BS; /*if(range < 50*BS) range = range * 0.5 + 25*BS;*/ - // Move the invisible limit a bit further - //range *= 1.2; } driver->setFog( diff --git a/src/map.cpp b/src/map.cpp index febc40d..ba4130c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3659,13 +3659,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Take a fair amount as we will be dropping more out later // Umm... these additions are a bit strange but they are needed. v3s16 p_blocks_min( - p_nodes_min.X / MAP_BLOCKSIZE - 2, - p_nodes_min.Y / MAP_BLOCKSIZE - 2, - p_nodes_min.Z / MAP_BLOCKSIZE - 2); + p_nodes_min.X / MAP_BLOCKSIZE - 3, + p_nodes_min.Y / MAP_BLOCKSIZE - 3, + p_nodes_min.Z / MAP_BLOCKSIZE - 3); v3s16 p_blocks_max( - p_nodes_max.X / MAP_BLOCKSIZE + 0, - p_nodes_max.Y / MAP_BLOCKSIZE + 0, - p_nodes_max.Z / MAP_BLOCKSIZE + 0); + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); u32 vertex_count = 0; u32 meshbuffer_count = 0; From d47120aeb3bf344be3f414a1a3505bd772eebf54 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 18 Oct 2011 23:55:55 +0300 Subject: [PATCH 33/40] Add enable_2d_clouds setting for usage on lower-end machines --- src/clouds.cpp | 9 +++++++-- src/defaultsettings.cpp | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/clouds.cpp b/src/clouds.cpp index 8981b42..063a4b1 100644 --- a/src/clouds.cpp +++ b/src/clouds.cpp @@ -21,8 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "constants.h" #include "debug.h" +#include "main.h" // For g_profiler and g_settings #include "profiler.h" -#include "main.h" // For g_profiler +#include "settings.h" Clouds::Clouds( scene::ISceneNode* parent, @@ -80,6 +81,10 @@ void Clouds::render() ScopeProfiler sp(g_profiler, "Rendering of clouds, avg", SPT_AVG); + int num_faces_to_draw = 6; + if(g_settings->getBool("enable_2d_clouds")) + num_faces_to_draw = 1; + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setMaterial(m_material); @@ -148,7 +153,7 @@ void Clouds::render() f32 ry = 8*BS; f32 rz = cloud_size; - for(int i=0;i<6;i++) + for(int i=0; isetDefault("invisible_stone", "false"); settings->setDefault("screenshot_path", "."); settings->setDefault("view_bobbing_amount", "1.0"); + settings->setDefault("enable_2d_clouds", "false"); // Server stuff // "map-dir" doesn't exist by default. From 3a06fb88314cff3dd0c96665229b47af0f01b276 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 19 Oct 2011 02:17:23 +0300 Subject: [PATCH 34/40] Fix and tune block sending --- minetest.conf.example | 4 +- src/client.cpp | 2 +- src/defaultsettings.cpp | 4 +- src/server.cpp | 114 +++++++++++++++++++--------------------- 4 files changed, 59 insertions(+), 65 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index 6f817d3..30adc5e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -143,8 +143,8 @@ #active_block_range = 2 #max_simultaneous_block_sends_per_client = 2 #max_simultaneous_block_sends_server_total = 8 -#max_block_send_distance = 8 -#max_block_generate_distance = 8 +#max_block_send_distance = 7 +#max_block_generate_distance = 5 #time_send_interval = 20 # Length of day/night cycle. 72=20min, 360=4min, 1=24hour #time_speed = 72 diff --git a/src/client.cpp b/src/client.cpp index 816ab39..5ec53a5 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -156,7 +156,7 @@ void * MeshUpdateThread::Thread() continue; } - ScopeProfiler sp(g_profiler, "mesh make"); + ScopeProfiler sp(g_profiler, "Client: Mesh making"); scene::SMesh *mesh_new = NULL; mesh_new = makeMapBlockMesh(q->data); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 8429ff2..7f0d46a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -101,8 +101,8 @@ void set_default_settings(Settings *settings) // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); settings->setDefault("max_simultaneous_block_sends_server_total", "8"); - settings->setDefault("max_block_send_distance", "8"); - settings->setDefault("max_block_generate_distance", "8"); + settings->setDefault("max_block_send_distance", "7"); + settings->setDefault("max_block_generate_distance", "5"); settings->setDefault("time_send_interval", "20"); settings->setDefault("time_speed", "96"); settings->setDefault("server_unload_unused_data_timeout", "60"); diff --git a/src/server.cpp b/src/server.cpp index 3a3d0b8..1a441e8 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -354,11 +354,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, // Increment timers m_nothing_to_send_pause_timer -= dtime; + m_nearest_unsent_reset_timer += dtime; if(m_nothing_to_send_pause_timer >= 0) { - // Keep this reset - m_nearest_unsent_reset_timer = 0; return; } @@ -410,17 +409,13 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /*infostream<<"m_nearest_unsent_reset_timer=" < 10.0) + // Reset periodically to workaround for some bugs or stuff + if(m_nearest_unsent_reset_timer > 20.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; - /*infostream<<"Resetting m_nearest_unsent_d for " - <getPlayerName(peer_id)<getPlayerName(peer_id)<getS16("max_block_generate_distance"); // Don't loop very much at a time - if(d_max > d_start+1) - d_max = d_start+1; + s16 max_d_increment_at_time = 2; + if(d_max > d_start + max_d_increment_at_time) + d_max = d_start + max_d_increment_at_time; /*if(d_max_gen > d_start+2) d_max_gen = d_start+2;*/ //infostream<<"Starting from "<getPlayerName(peer_id)<m_emerge_queue.addBlock(peer_id, p, flags); server->m_emergethread.trigger(); + + if(nearest_emerged_d == -1) + nearest_emerged_d = d; + } else { + if(nearest_emergefull_d == -1) + nearest_emergefull_d = d; } // get next one. continue; } + if(nearest_sent_d == -1) + nearest_sent_d = d; + /* Add block to send queue */ + /*errorstream<<"sending from d="<getPlayerName(peer_id)< g_settings->getS16("max_block_send_distance")){ + new_nearest_unsent_d = 0; + m_nothing_to_send_pause_timer = 2.0; + /*infostream<<"GetNextBlocks(): d wrapped around for " + <getPlayerName(peer_id) + <<"; setting to 0 and pausing"<= - g_settings->getS16("max_block_send_distance")) - { - // Pause time in seconds - m_nothing_to_send_pause_timer = 1.0; - /*infostream<<"nothing to send to " - <getPlayerName(peer_id) - <<" (d="< Date: Wed, 19 Oct 2011 02:36:46 +0300 Subject: [PATCH 35/40] Fix apple inventory texture --- src/content_inventory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content_inventory.cpp b/src/content_inventory.cpp index 59997ee..413ae85 100644 --- a/src/content_inventory.cpp +++ b/src/content_inventory.cpp @@ -72,8 +72,8 @@ std::string item_craft_get_image_name(const std::string &subname) else if(subname == "firefly") return "firefly.png"; else if(subname == "apple") - return "apple.png"; - else if(subname == "apple_iron") + return "apple.png^[forcesingle"; + else if(subname == "apple_iron") return "apple_iron.png"; else return "cloud.png"; // just something From b6fcbc5fbaba4a7faa65f792b16e47a405fa4ebf Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Wed, 19 Oct 2011 19:34:47 +0300 Subject: [PATCH 36/40] Default max_simultaneous_block_sends_server_total to 2 to make network not cough too much on the fixed block sending code --- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index 30adc5e..01b3f3e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -142,7 +142,7 @@ #active_object_send_range_blocks = 3 #active_block_range = 2 #max_simultaneous_block_sends_per_client = 2 -#max_simultaneous_block_sends_server_total = 8 +#max_simultaneous_block_sends_server_total = 2 #max_block_send_distance = 7 #max_block_generate_distance = 5 #time_send_interval = 20 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 7f0d46a..586eaa2 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -100,7 +100,7 @@ void set_default_settings(Settings *settings) //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); - settings->setDefault("max_simultaneous_block_sends_server_total", "8"); + settings->setDefault("max_simultaneous_block_sends_server_total", "2"); settings->setDefault("max_block_send_distance", "7"); settings->setDefault("max_block_generate_distance", "5"); settings->setDefault("time_send_interval", "20"); From 4b6138e69b65271b0e568f821a4d1bd285affedd Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 20 Oct 2011 23:04:09 +0300 Subject: [PATCH 37/40] Improve Connection with threading and some kind of congestion control --- minetest.conf.example | 2 +- src/client.cpp | 23 +- src/client.h | 10 +- src/connection.cpp | 1416 +++++++++++++++++++++++---------------- src/connection.h | 289 +++++--- src/defaultsettings.cpp | 2 +- src/main.cpp | 2 + src/map.cpp | 2 + src/server.cpp | 60 +- src/server.h | 4 +- src/servercommand.cpp | 21 +- src/test.cpp | 48 +- 12 files changed, 1163 insertions(+), 716 deletions(-) diff --git a/minetest.conf.example b/minetest.conf.example index 01b3f3e..30adc5e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -142,7 +142,7 @@ #active_object_send_range_blocks = 3 #active_block_range = 2 #max_simultaneous_block_sends_per_client = 2 -#max_simultaneous_block_sends_server_total = 2 +#max_simultaneous_block_sends_server_total = 8 #max_block_send_distance = 7 #max_block_generate_distance = 5 #time_send_interval = 20 diff --git a/src/client.cpp b/src/client.cpp index 5ec53a5..a777293 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -246,7 +246,7 @@ void Client::connect(Address address) { DSTACK(__FUNCTION_NAME); //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.setTimeoutMs(0); + m_con.SetTimeoutMs(0); m_con.Connect(address); } @@ -563,8 +563,8 @@ void Client::step(float dtime) counter = 0.0; //JMutexAutoLock lock(m_con_mutex); //bulk comment-out // connectedAndInitialized() is true, peer exists. - con::Peer *peer = m_con.GetPeer(PEER_ID_SERVER); - infostream<<"Client: avg_rtt="<avg_rtt<= 0.0){ + if(rtt < 0.01){ + if(m_max_packets_per_second < 100) + m_max_packets_per_second += 10; + } else if(rtt < 0.2){ + if(m_max_packets_per_second < 100) + m_max_packets_per_second += 2; + } else { + if(m_max_packets_per_second > 5) + m_max_packets_per_second *= 0.5; + } + } + if(rtt < -0.999) {} else if(avg_rtt < 0.0) @@ -485,461 +503,210 @@ void Peer::reportRTT(float rtt) Connection */ -Connection::Connection( - u32 protocol_id, - u32 max_packet_size, - float timeout, - PeerHandler *peerhandler -) +Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout): + m_protocol_id(protocol_id), + m_max_packet_size(max_packet_size), + m_timeout(timeout), + m_peer_id(0), + m_bc_peerhandler(NULL), + m_bc_receive_timeout(0), + m_indentation(0) { - assert(peerhandler != NULL); + m_socket.setTimeoutMs(5); - m_protocol_id = protocol_id; - m_max_packet_size = max_packet_size; - m_timeout = timeout; - m_peer_id = PEER_ID_INEXISTENT; - //m_waiting_new_peer_id = false; - m_indentation = 0; - m_peerhandler = peerhandler; + Start(); } +Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, + PeerHandler *peerhandler): + m_protocol_id(protocol_id), + m_max_packet_size(max_packet_size), + m_timeout(timeout), + m_peer_id(0), + m_bc_peerhandler(peerhandler), + m_bc_receive_timeout(0), + m_indentation(0) +{ + m_socket.setTimeoutMs(5); + + Start(); +} + + Connection::~Connection() { - // Clear peers - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + stop(); +} + +/* Internal stuff */ + +void * Connection::Thread() +{ + ThreadStarted(); + log_register_thread("Connection"); + + dout_con<<"Connection thread started"< 0.1) + dtime = 0.1; + if(dtime < 0.0) + dtime = 0.0; + + runTimeouts(dtime); + + while(m_command_queue.size() != 0){ + ConnectionCommand c = m_command_queue.pop_front(); + processCommand(c); + } + + send(dtime); + + receive(); + + END_DEBUG_EXCEPTION_HANDLER(derr_con); + } + + return NULL; +} + +void Connection::putEvent(ConnectionEvent &e) +{ + assert(e.type != CONNEVENT_NONE); + m_event_queue.push_back(e); +} + +void Connection::processCommand(ConnectionCommand &c) +{ + switch(c.type){ + case CONNCMD_NONE: + dout_con<::Iterator + j = m_peers.getIterator(); + j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); - delete peer; + peer->m_sendtime_accu += dtime; + peer->m_num_sent = 0; + peer->m_max_num_sent = peer->m_sendtime_accu * + peer->m_max_packets_per_second; } -} - -void Connection::Serve(unsigned short port) -{ - m_socket.Bind(port); - m_peer_id = PEER_ID_SERVER; -} - -void Connection::Connect(Address address) -{ - core::map::Node *node = m_peers.find(PEER_ID_SERVER); - if(node != NULL){ - throw ConnectionException("Already connected to a server"); + Queue postponed_packets; + while(m_outgoing_queue.size() != 0){ + OutgoingPacket packet = m_outgoing_queue.pop_front(); + Peer *peer = getPeerNoEx(packet.peer_id); + if(!peer) + continue; + if(peer->channels[packet.channelnum].outgoing_reliables.size() >= 5){ + postponed_packets.push_back(packet); + } else if(peer->m_num_sent < peer->m_max_num_sent){ + rawSendAsPacket(packet.peer_id, packet.channelnum, + packet.data, packet.reliable); + peer->m_num_sent++; + } else { + postponed_packets.push_back(packet); + } } - - Peer *peer = new Peer(PEER_ID_SERVER, address); - m_peers.insert(peer->id, peer); - m_peerhandler->peerAdded(peer); - - m_socket.Bind(0); - - // Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT - m_peer_id = PEER_ID_INEXISTENT; - SharedBuffer data(0); - Send(PEER_ID_SERVER, 0, data, true); - - //m_waiting_new_peer_id = true; -} - -void Connection::Disconnect() -{ - // Create and send DISCO packet - SharedBuffer data(2); - writeU8(&data[0], TYPE_CONTROL); - writeU8(&data[1], CONTROLTYPE_DISCO); - - // Send to all - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + while(postponed_packets.size() != 0){ + m_outgoing_queue.push_back(postponed_packets.pop_front()); + } + for(core::map::Iterator + j = m_peers.getIterator(); + j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); - SendAsPacket(peer->id, 0, data, false); + peer->m_sendtime_accu -= (float)peer->m_num_sent / + peer->m_max_packets_per_second; + if(peer->m_sendtime_accu > 10. / peer->m_max_packets_per_second) + peer->m_sendtime_accu = 10. / peer->m_max_packets_per_second; } } -bool Connection::Connected() +// Receive packets from the network and buffers and create ConnectionEvents +void Connection::receive() { - if(m_peers.size() != 1) - return false; - - core::map::Node *node = m_peers.find(PEER_ID_SERVER); - if(node == NULL) - return false; - - if(m_peer_id == PEER_ID_INEXISTENT) - return false; - - return true; -} - -SharedBuffer Channel::ProcessPacket( - SharedBuffer packetdata, - Connection *con, - u16 peer_id, - u8 channelnum, - bool reliable) -{ - IndentationRaiser iraiser(&(con->m_indentation)); - - if(packetdata.getSize() < 1) - throw InvalidIncomingDataException("packetdata.getSize() < 1"); - - u8 type = readU8(&packetdata[0]); - - if(type == TYPE_CONTROL) - { - if(packetdata.getSize() < 2) - throw InvalidIncomingDataException("packetdata.getSize() < 2"); - - u8 controltype = readU8(&packetdata[1]); - - if(controltype == CONTROLTYPE_ACK) - { - if(packetdata.getSize() < 4) - throw InvalidIncomingDataException - ("packetdata.getSize() < 4 (ACK header size)"); - - u16 seqnum = readU16(&packetdata[2]); - con->PrintInfo(); - dout_con<<"Got CONTROLTYPE_ACK: channelnum=" - <<((int)channelnum&0xff)<<", peer_id="<PrintInfo(); - outgoing_reliables.print(); - dout_con<PrintInfo(derr_con); - derr_con<<"WARNING: ACKed packet not " - "in outgoing queue" - <PrintInfo(); - dout_con<<"Got new peer id: "<GetPeerID() != PEER_ID_INEXISTENT) - { - con->PrintInfo(derr_con); - derr_con<<"WARNING: Not changing" - " existing peer id."<SetPeerID(peer_id_new); - } - throw ProcessedSilentlyException("Got a SET_PEER_ID"); - } - else if(controltype == CONTROLTYPE_PING) - { - // Just ignore it, the incoming data already reset - // the timeout counter - con->PrintInfo(); - dout_con<<"PING"<PrintInfo(); - dout_con<<"DISCO: Removing peer "<<(peer_id)<deletePeer(peer_id, false) == false) - { - con->PrintInfo(derr_con); - derr_con<<"DISCO: Peer not found"<PrintInfo(derr_con); - derr_con<<"INVALID TYPE_CONTROL: invalid controltype=" - <<((int)controltype&0xff)<PrintInfo(); - dout_con<<"RETURNING TYPE_ORIGINAL to user" - < payload(packetdata.getSize() - ORIGINAL_HEADER_SIZE); - memcpy(*payload, &packetdata[ORIGINAL_HEADER_SIZE], payload.getSize()); - return payload; - } - else if(type == TYPE_SPLIT) - { - // We have to create a packet again for buffering - // This isn't actually too bad an idea. - BufferedPacket packet = makePacket( - con->GetPeer(peer_id)->address, - packetdata, - con->GetProtocolID(), - peer_id, - channelnum); - // Buffer the packet - SharedBuffer data = incoming_splits.insert(packet, reliable); - if(data.getSize() != 0) - { - con->PrintInfo(); - dout_con<<"RETURNING TYPE_SPLIT: Constructed full data, " - <<"size="<PrintInfo(); - dout_con<<"BUFFERED TYPE_SPLIT"<PrintInfo(); - if(is_future_packet) - dout_con<<"BUFFERING"; - else if(is_old_packet) - dout_con<<"OLD"; - else - dout_con<<"RECUR"; - dout_con<<" TYPE_RELIABLE seqnum="< reply(4); - writeU8(&reply[0], TYPE_CONTROL); - writeU8(&reply[1], CONTROLTYPE_ACK); - writeU16(&reply[2], seqnum); - con->SendAsPacket(peer_id, channelnum, reply, false); - - //if(seqnum_higher(seqnum, next_incoming_seqnum)) - if(is_future_packet) - { - /*con->PrintInfo(); - dout_con<<"Buffering reliable packet (seqnum=" - <GetPeer(peer_id)->address, - packetdata, - con->GetProtocolID(), - peer_id, - channelnum); - try{ - incoming_reliables.insert(packet); - - /*con->PrintInfo(); - dout_con<<"INCOMING: "; - incoming_reliables.print(); - dout_con< payload(packetdata.getSize() - RELIABLE_HEADER_SIZE); - memcpy(*payload, &packetdata[RELIABLE_HEADER_SIZE], payload.getSize()); - - return ProcessPacket(payload, con, peer_id, channelnum, true); - } - else - { - con->PrintInfo(derr_con); - derr_con<<"Got invalid type="<<((int)type&0xff)< Channel::CheckIncomingBuffers(Connection *con, - u16 &peer_id) -{ - u16 firstseqnum = 0; - // Clear old packets from start of buffer - try{ - for(;;){ - firstseqnum = incoming_reliables.getFirstSeqnum(); - if(seqnum_higher(next_incoming_seqnum, firstseqnum)) - incoming_reliables.popFirst(); - else - break; - } - // This happens if all packets are old - }catch(con::NotFoundException) - {} - - if(incoming_reliables.empty() == false) - { - if(firstseqnum == next_incoming_seqnum) - { - BufferedPacket p = incoming_reliables.popFirst(); - - peer_id = readPeerId(*p.data); - u8 channelnum = readChannel(*p.data); - u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); - - con->PrintInfo(); - dout_con<<"UNBUFFERING TYPE_RELIABLE" - <<" seqnum="< Connection::GetFromBuffers(u16 &peer_id) -{ - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) - { - Peer *peer = j.getNode()->getValue(); - for(u16 i=0; ichannels[i]; - try{ - SharedBuffer resultdata = channel->CheckIncomingBuffers - (this, peer_id); - - return resultdata; - } - catch(NoIncomingDataException &e) - { - } - catch(InvalidIncomingDataException &e) - { - } - catch(ProcessedSilentlyException &e) - { - } - } - } - throw NoIncomingDataException("No relevant data in buffers"); -} - -u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) -{ - /* - Receive a packet from the network - */ - + u32 datasize = 100000; // TODO: We can not know how many layers of header there are. // For now, just assume there are no other than the base headers. u32 packet_maxsize = datasize + BASE_HEADER_SIZE; Buffer packetdata(packet_maxsize); + + bool single_wait_done = false; for(;;) { - try - { - /* - Check if some buffer has relevant data - */ - try{ - SharedBuffer resultdata = GetFromBuffers(peer_id); - - if(datasize < resultdata.getSize()) - throw InvalidIncomingDataException - ("Buffer too small for received data"); - - memcpy(data, *resultdata, resultdata.getSize()); - return resultdata.getSize(); - } - catch(NoIncomingDataException &e) + try{ + /* Check if some buffer has relevant data */ { + u16 peer_id; + SharedBuffer resultdata; + bool got = getFromBuffers(peer_id, resultdata); + if(got){ + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + putEvent(e); + continue; + } } - - Address sender; + + if(single_wait_done){ + if(m_socket.WaitData(0) == false) + break; + } + + single_wait_done = true; + Address sender; s32 received_size = m_socket.Receive(sender, *packetdata, packet_maxsize); if(received_size < 0) - throw NoIncomingDataException("No incoming data"); + break; if(received_size < BASE_HEADER_SIZE) - throw InvalidIncomingDataException("No full header received"); + continue; if(readU32(&packetdata[0]) != m_protocol_id) - throw InvalidIncomingDataException("Invalid protocol id"); + continue; - peer_id = readPeerId(*packetdata); + u16 peer_id = readPeerId(*packetdata); u8 channelnum = readChannel(*packetdata); if(channelnum > CHANNEL_COUNT-1){ PrintInfo(derr_con); @@ -999,17 +766,23 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) /* Find an unused peer id */ + bool out_of_ids = false; for(;;) { // Check if exists if(m_peers.find(peer_id_new) == NULL) break; // Check for overflow - if(peer_id_new == 65535) - throw ConnectionException - ("Connection ran out of peer ids"); + if(peer_id_new == 65535){ + out_of_ids = true; + break; + } peer_id_new++; } + if(out_of_ids){ + errorstream<id, peer); - m_peerhandler->peerAdded(peer); + + // Create peer addition event + ConnectionEvent e; + e.peerAdded(peer_id_new, sender); + putEvent(e); // Create CONTROL packet to tell the peer id to the new peer. SharedBuffer reply(4); writeU8(&reply[0], TYPE_CONTROL); writeU8(&reply[1], CONTROLTYPE_SET_PEER_ID); writeU16(&reply[2], peer_id_new); - SendAsPacket(peer_id_new, 0, reply, true); + sendAsPacket(peer_id_new, 0, reply, true); // We're now talking to a valid peer_id peer_id = peer_id_new; @@ -1053,12 +830,7 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) PrintInfo(derr_con); derr_con<<"Peer "<timeout_counter = 0.0; @@ -1074,8 +846,8 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) try{ // Process it (the result is some data with no headers made by us) - SharedBuffer resultdata = channel->ProcessPacket - (strippeddata, this, peer_id, channelnum); + SharedBuffer resultdata = processPacket + (channel, strippeddata, peer_id, channelnum, false); PrintInfo(); dout_con<<"ProcessPacket returned data of size " @@ -1085,123 +857,20 @@ u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) throw InvalidIncomingDataException ("Buffer too small for received data"); - memcpy(data, *resultdata, resultdata.getSize()); - return resultdata.getSize(); - } - catch(ProcessedSilentlyException &e) - { - // If there is more data, receive again - if(m_socket.WaitData(0) == true) - continue; - } - throw NoIncomingDataException("No incoming data (2)"); - } // try - catch(InvalidIncomingDataException &e) - { - // If there is more data, receive again - if(m_socket.WaitData(0) == true) + ConnectionEvent e; + e.dataReceived(peer_id, resultdata); + putEvent(e); continue; + }catch(ProcessedSilentlyException &e){ + } + }catch(InvalidIncomingDataException &e){ } - catch(SendFailedException &e) - { + catch(ProcessedSilentlyException &e){ } } // for } -void Connection::SendToAll(u8 channelnum, SharedBuffer data, bool reliable) -{ - core::map::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) - { - Peer *peer = j.getNode()->getValue(); - Send(peer->id, channelnum, data, reliable); - } -} - -void Connection::Send(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable) -{ - assert(channelnum < CHANNEL_COUNT); - - Peer *peer = GetPeerNoEx(peer_id); - if(peer == NULL) - return; - Channel *channel = &(peer->channels[channelnum]); - - u32 chunksize_max = m_max_packet_size - BASE_HEADER_SIZE; - if(reliable) - chunksize_max -= RELIABLE_HEADER_SIZE; - - core::list > originals; - originals = makeAutoSplitPacket(data, chunksize_max, - channel->next_outgoing_split_seqnum); - - core::list >::Iterator i; - i = originals.begin(); - for(; i != originals.end(); i++) - { - SharedBuffer original = *i; - - SendAsPacket(peer_id, channelnum, original, reliable); - } -} - -void Connection::SendAsPacket(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable) -{ - Peer *peer = GetPeer(peer_id); - Channel *channel = &(peer->channels[channelnum]); - - if(reliable) - { - u16 seqnum = channel->next_outgoing_seqnum; - channel->next_outgoing_seqnum++; - - SharedBuffer reliable = makeReliablePacket(data, seqnum); - - // Add base headers and make a packet - BufferedPacket p = makePacket(peer->address, reliable, - m_protocol_id, m_peer_id, channelnum); - - try{ - // Buffer the packet - channel->outgoing_reliables.insert(p); - } - catch(AlreadyExistsException &e) - { - PrintInfo(derr_con); - derr_con<<"WARNING: Going to send a reliable packet " - "seqnum="<address, data, - m_protocol_id, m_peer_id, channelnum); - - // Send the packet - RawSend(p); - } -} - -void Connection::RawSend(const BufferedPacket &packet) -{ - try{ - m_socket.Send(packet.address, *packet.data, packet.data.getSize()); - } catch(SendFailedException &e){ - derr_con<<"Connection::RawSend(): SendFailedException: " - < timeouted_peers; core::map::Iterator j; @@ -1278,7 +947,7 @@ void Connection::RunTimeouts(float dtime) <<", seqnum="< data(2); writeU8(&data[0], TYPE_CONTROL); writeU8(&data[1], CONTROLTYPE_PING); - SendAsPacket(peer->id, 0, data, true); + rawSendAsPacket(peer->id, 0, data, true); peer->ping_timer = 0.0; } @@ -1317,12 +986,167 @@ nextpeer: } } -Peer* Connection::GetPeer(u16 peer_id) +void Connection::serve(u16 port) +{ + dout_con<::Node *node = m_peers.find(PEER_ID_SERVER); + if(node != NULL){ + throw ConnectionException("Already connected to a server"); + } + + Peer *peer = new Peer(PEER_ID_SERVER, address); + m_peers.insert(peer->id, peer); + + // Create event + ConnectionEvent e; + e.peerAdded(peer->id, peer->address); + putEvent(e); + + m_socket.Bind(0); + + // Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT + m_peer_id = PEER_ID_INEXISTENT; + SharedBuffer data(0); + Send(PEER_ID_SERVER, 0, data, true); +} + +void Connection::disconnect() +{ + dout_con< data(2); + writeU8(&data[0], TYPE_CONTROL); + writeU8(&data[1], CONTROLTYPE_DISCO); + + // Send to all + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + rawSendAsPacket(peer->id, 0, data, false); + } +} + +void Connection::sendToAll(u8 channelnum, SharedBuffer data, bool reliable) +{ + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + send(peer->id, channelnum, data, reliable); + } +} + +void Connection::send(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable) +{ + dout_con<address, data, + m_protocol_id, m_peer_id, channelnum); + + // Send the packet + rawSend(p); + } +} + +void Connection::rawSend(const BufferedPacket &packet) +{ + try{ + m_socket.Send(packet.address, *packet.data, packet.data.getSize()); + } catch(SendFailedException &e){ + derr_con<<"Connection::rawSend(): SendFailedException: " + <::Node *node = m_peers.find(peer_id); if(node == NULL){ - // Peer not found throw PeerNotFoundException("GetPeer: Peer not found (possible timeout)"); } @@ -1332,7 +1156,7 @@ Peer* Connection::GetPeer(u16 peer_id) return node->getValue(); } -Peer* Connection::GetPeerNoEx(u16 peer_id) +Peer* Connection::getPeerNoEx(u16 peer_id) { core::map::Node *node = m_peers.find(peer_id); @@ -1346,7 +1170,7 @@ Peer* Connection::GetPeerNoEx(u16 peer_id) return node->getValue(); } -core::list Connection::GetPeers() +core::list Connection::getPeers() { core::list list; core::map::Iterator j; @@ -1359,23 +1183,474 @@ core::list Connection::GetPeers() return list; } +bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer &dst) +{ + core::map::Iterator j; + j = m_peers.getIterator(); + for(; j.atEnd() == false; j++) + { + Peer *peer = j.getNode()->getValue(); + for(u16 i=0; ichannels[i]; + SharedBuffer resultdata; + bool got = checkIncomingBuffers(channel, peer_id, resultdata); + if(got){ + dst = resultdata; + return true; + } + } + } + return false; +} + +bool Connection::checkIncomingBuffers(Channel *channel, u16 &peer_id, + SharedBuffer &dst) +{ + u16 firstseqnum = 0; + // Clear old packets from start of buffer + try{ + for(;;){ + firstseqnum = channel->incoming_reliables.getFirstSeqnum(); + if(seqnum_higher(channel->next_incoming_seqnum, firstseqnum)) + channel->incoming_reliables.popFirst(); + else + break; + } + // This happens if all packets are old + }catch(con::NotFoundException) + {} + + if(channel->incoming_reliables.empty() == false) + { + if(firstseqnum == channel->next_incoming_seqnum) + { + BufferedPacket p = channel->incoming_reliables.popFirst(); + + peer_id = readPeerId(*p.data); + u8 channelnum = readChannel(*p.data); + u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); + + PrintInfo(); + dout_con<<"UNBUFFERING TYPE_RELIABLE" + <<" seqnum="<outgoing_reliables.print(); + dout_con< payload(packetdata.getSize() - ORIGINAL_HEADER_SIZE); + memcpy(*payload, &packetdata[ORIGINAL_HEADER_SIZE], payload.getSize()); + return payload; + } + else if(type == TYPE_SPLIT) + { + // We have to create a packet again for buffering + // This isn't actually too bad an idea. + BufferedPacket packet = makePacket( + getPeer(peer_id)->address, + packetdata, + GetProtocolID(), + peer_id, + channelnum); + // Buffer the packet + SharedBuffer data = channel->incoming_splits.insert(packet, reliable); + if(data.getSize() != 0) + { + PrintInfo(); + dout_con<<"RETURNING TYPE_SPLIT: Constructed full data, " + <<"size="<next_incoming_seqnum); + bool is_old_packet = seqnum_higher(channel->next_incoming_seqnum, seqnum); + + PrintInfo(); + if(is_future_packet) + dout_con<<"BUFFERING"; + else if(is_old_packet) + dout_con<<"OLD"; + else + dout_con<<"RECUR"; + dout_con<<" TYPE_RELIABLE seqnum="<incoming_reliables.size() < 100); + + // Send a CONTROLTYPE_ACK + SharedBuffer reply(4); + writeU8(&reply[0], TYPE_CONTROL); + writeU8(&reply[1], CONTROLTYPE_ACK); + writeU16(&reply[2], seqnum); + rawSendAsPacket(peer_id, channelnum, reply, false); + + //if(seqnum_higher(seqnum, channel->next_incoming_seqnum)) + if(is_future_packet) + { + /*PrintInfo(); + dout_con<<"Buffering reliable packet (seqnum=" + <address, + packetdata, + GetProtocolID(), + peer_id, + channelnum); + try{ + channel->incoming_reliables.insert(packet); + + /*PrintInfo(); + dout_con<<"INCOMING: "; + channel->incoming_reliables.print(); + dout_con<next_incoming_seqnum, seqnum)) + else if(is_old_packet) + { + // An old packet, dump it + throw InvalidIncomingDataException("Got an old reliable packet"); + } + + channel->next_incoming_seqnum++; + + // Get out the inside packet and re-process it + SharedBuffer payload(packetdata.getSize() - RELIABLE_HEADER_SIZE); + memcpy(*payload, &packetdata[RELIABLE_HEADER_SIZE], payload.getSize()); + + return processPacket(channel, payload, peer_id, channelnum, true); + } + else + { + PrintInfo(derr_con); + derr_con<<"Got invalid type="<<((int)type&0xff)<deletingPeer(m_peers[peer_id], timeout); + + Peer *peer = m_peers[peer_id]; + + // Create event + ConnectionEvent e; + e.peerRemoved(peer_id, timeout, peer->address); + putEvent(e); + delete m_peers[peer_id]; m_peers.remove(peer_id); return true; } +/* Interface */ + +ConnectionEvent Connection::getEvent() +{ + if(m_event_queue.size() == 0){ + ConnectionEvent e; + e.type = CONNEVENT_NONE; + return e; + } + return m_event_queue.pop_front(); +} + +ConnectionEvent Connection::waitEvent(u32 timeout_ms) +{ + try{ + return m_event_queue.pop_front(timeout_ms); + } catch(ItemNotFoundException &e){ + ConnectionEvent e; + e.type = CONNEVENT_NONE; + return e; + } +} + +void Connection::putCommand(ConnectionCommand &c) +{ + m_command_queue.push_back(c); +} + +void Connection::Serve(unsigned short port) +{ + ConnectionCommand c; + c.serve(port); + putCommand(c); +} + +void Connection::Connect(Address address) +{ + ConnectionCommand c; + c.connect(address); + putCommand(c); +} + +bool Connection::Connected() +{ + JMutexAutoLock peerlock(m_peers_mutex); + + if(m_peers.size() != 1) + return false; + + core::map::Node *node = m_peers.find(PEER_ID_SERVER); + if(node == NULL) + return false; + + if(m_peer_id == PEER_ID_INEXISTENT) + return false; + + return true; +} + +void Connection::Disconnect() +{ + ConnectionCommand c; + c.disconnect(); + putCommand(c); +} + +u32 Connection::Receive(u16 &peer_id, u8 *data, u32 datasize) +{ + for(;;){ + ConnectionEvent e = waitEvent(m_bc_receive_timeout); + if(e.type != CONNEVENT_NONE) + dout_con<peerAdded(&tmp); + continue; } + case CONNEVENT_PEER_REMOVED: { + Peer tmp(e.peer_id, e.address); + if(m_bc_peerhandler) + m_bc_peerhandler->deletingPeer(&tmp, e.timeout); + continue; } + } + } + throw NoIncomingDataException("No incoming data"); +} + +void Connection::SendToAll(u8 channelnum, SharedBuffer data, bool reliable) +{ + assert(channelnum < CHANNEL_COUNT); + + ConnectionCommand c; + c.sendToAll(channelnum, data, reliable); + putCommand(c); +} + +void Connection::Send(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable) +{ + assert(channelnum < CHANNEL_COUNT); + + ConnectionCommand c; + c.send(peer_id, channelnum, data, reliable); + putCommand(c); +} + +void Connection::RunTimeouts(float dtime) +{ + // No-op +} + +Address Connection::GetPeerAddress(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->address; +} + +float Connection::GetPeerAvgRTT(u16 peer_id) +{ + JMutexAutoLock peerlock(m_peers_mutex); + return getPeer(peer_id)->avg_rtt; +} + +void Connection::DeletePeer(u16 peer_id) +{ + ConnectionCommand c; + c.deletePeer(peer_id); + putCommand(c); +} + void Connection::PrintInfo(std::ostream &out) { - out< ProcessPacket( - SharedBuffer packetdata, - Connection *con, - u16 peer_id, - u8 channelnum, - bool reliable=false); - - // Returns next data from a buffer if possible - // throws a NoIncomingDataException if no data is available - // If found, sets peer_id - SharedBuffer CheckIncomingBuffers(Connection *con, - u16 &peer_id); u16 next_outgoing_seqnum; u16 next_incoming_seqnum; @@ -412,78 +390,237 @@ public: // with the id we have given to it bool has_sent_with_id; + float m_sendtime_accu; + float m_max_packets_per_second; + int m_num_sent; + int m_max_num_sent; + private: }; -class Connection +/* + Connection +*/ + +struct OutgoingPacket +{ + u16 peer_id; + u8 channelnum; + SharedBuffer data; + bool reliable; + + OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer data_, + bool reliable_): + peer_id(peer_id_), + channelnum(channelnum_), + data(data_), + reliable(reliable_) + { + } +}; + +enum ConnectionEventType{ + CONNEVENT_NONE, + CONNEVENT_DATA_RECEIVED, + CONNEVENT_PEER_ADDED, + CONNEVENT_PEER_REMOVED, +}; + +struct ConnectionEvent +{ + enum ConnectionEventType type; + u16 peer_id; + SharedBuffer data; + bool timeout; + Address address; + + ConnectionEvent(): type(CONNEVENT_NONE) {} + + std::string describe() + { + switch(type){ + case CONNEVENT_NONE: + return "CONNEVENT_NONE"; + case CONNEVENT_DATA_RECEIVED: + return "CONNEVENT_DATA_RECEIVED"; + case CONNEVENT_PEER_ADDED: + return "CONNEVENT_PEER_ADDED"; + case CONNEVENT_PEER_REMOVED: + return "CONNEVENT_PEER_REMOVED"; + } + return "Invalid ConnectionEvent"; + } + + void dataReceived(u16 peer_id_, SharedBuffer data_) + { + type = CONNEVENT_DATA_RECEIVED; + peer_id = peer_id_; + data = data_; + } + void peerAdded(u16 peer_id_, Address address_) + { + type = CONNEVENT_PEER_ADDED; + peer_id = peer_id_; + address = address_; + } + void peerRemoved(u16 peer_id_, bool timeout_, Address address_) + { + type = CONNEVENT_PEER_REMOVED; + peer_id = peer_id_; + timeout = timeout_; + address = address_; + } +}; + +enum ConnectionCommandType{ + CONNCMD_NONE, + CONNCMD_SERVE, + CONNCMD_CONNECT, + CONNCMD_DISCONNECT, + CONNCMD_SEND, + CONNCMD_SEND_TO_ALL, + CONNCMD_DELETE_PEER, +}; + +struct ConnectionCommand +{ + enum ConnectionCommandType type; + u16 port; + Address address; + u16 peer_id; + u8 channelnum; + SharedBuffer data; + bool reliable; + + ConnectionCommand(): type(CONNCMD_NONE) {} + + void serve(u16 port_) + { + type = CONNCMD_SERVE; + port = port_; + } + void connect(Address address_) + { + type = CONNCMD_CONNECT; + address = address_; + } + void disconnect() + { + type = CONNCMD_DISCONNECT; + } + void send(u16 peer_id_, u8 channelnum_, + SharedBuffer data_, bool reliable_) + { + type = CONNCMD_SEND; + peer_id = peer_id_; + channelnum = channelnum_; + data = data_; + reliable = reliable_; + } + void sendToAll(u8 channelnum_, SharedBuffer data_, bool reliable_) + { + type = CONNCMD_SEND_TO_ALL; + channelnum = channelnum_; + data = data_; + reliable = reliable_; + } + void deletePeer(u16 peer_id_) + { + type = CONNCMD_DELETE_PEER; + peer_id = peer_id_; + } +}; + +class Connection: public SimpleThread { public: - Connection( - u32 protocol_id, - u32 max_packet_size, - float timeout, - PeerHandler *peerhandler - ); + Connection(u32 protocol_id, u32 max_packet_size, float timeout); + Connection(u32 protocol_id, u32 max_packet_size, float timeout, + PeerHandler *peerhandler); ~Connection(); - void setTimeoutMs(int timeout){ m_socket.setTimeoutMs(timeout); } - // Start being a server + void * Thread(); + + /* Interface */ + + ConnectionEvent getEvent(); + ConnectionEvent waitEvent(u32 timeout_ms); + void putCommand(ConnectionCommand &c); + + void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; } void Serve(unsigned short port); - // Connect to a server void Connect(Address address); bool Connected(); - void Disconnect(); - - // Sets peer_id - SharedBuffer GetFromBuffers(u16 &peer_id); - - // The peer_id of sender is stored in peer_id - // Return value: I guess this always throws an exception or - // actually gets data - // May call PeerHandler methods u32 Receive(u16 &peer_id, u8 *data, u32 datasize); - - // These will automatically package the data as an original or split void SendToAll(u8 channelnum, SharedBuffer data, bool reliable); void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); - // Send data as a packet; it will be wrapped in base header and - // optionally to a reliable packet. - void SendAsPacket(u16 peer_id, u8 channelnum, - SharedBuffer data, bool reliable); - // Sends a raw packet - void RawSend(const BufferedPacket &packet); - - // May call PeerHandler methods - void RunTimeouts(float dtime); - - // Can throw a PeerNotFoundException - Peer* GetPeer(u16 peer_id); - // returns NULL if failed - Peer* GetPeerNoEx(u16 peer_id); - core::list GetPeers(); - - // Calls PeerHandler::deletingPeer - // Returns false if peer was not found - bool deletePeer(u16 peer_id, bool timeout); - - void SetPeerID(u16 id){ m_peer_id = id; } + void RunTimeouts(float dtime); // dummy u16 GetPeerID(){ return m_peer_id; } - u32 GetProtocolID(){ return m_protocol_id; } + Address GetPeerAddress(u16 peer_id); + float GetPeerAvgRTT(u16 peer_id); + void DeletePeer(u16 peer_id); + +private: + void putEvent(ConnectionEvent &e); + void processCommand(ConnectionCommand &c); + void send(float dtime); + void receive(); + void runTimeouts(float dtime); + void serve(u16 port); + void connect(Address address); + void disconnect(); + void sendToAll(u8 channelnum, SharedBuffer data, bool reliable); + void send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); + void sendAsPacket(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable); + void rawSendAsPacket(u16 peer_id, u8 channelnum, + SharedBuffer data, bool reliable); + void rawSend(const BufferedPacket &packet); + Peer* getPeer(u16 peer_id); + Peer* getPeerNoEx(u16 peer_id); + core::list getPeers(); + bool getFromBuffers(u16 &peer_id, SharedBuffer &dst); + // Returns next data from a buffer if possible + // If found, returns true; if not, false. + // If found, sets peer_id and dst + bool checkIncomingBuffers(Channel *channel, u16 &peer_id, + SharedBuffer &dst); + /* + Processes a packet with the basic header stripped out. + Parameters: + packetdata: Data in packet (with no base headers) + peer_id: peer id of the sender of the packet in question + channelnum: channel on which the packet was sent + reliable: true if recursing into a reliable packet + */ + SharedBuffer processPacket(Channel *channel, + SharedBuffer packetdata, u16 peer_id, + u8 channelnum, bool reliable); + bool deletePeer(u16 peer_id, bool timeout); + + Queue m_outgoing_queue; + MutexedQueue m_event_queue; + MutexedQueue m_command_queue; + + u32 m_protocol_id; + u32 m_max_packet_size; + float m_timeout; + UDPSocket m_socket; + u16 m_peer_id; + + core::map m_peers; + JMutex m_peers_mutex; - // For debug printing + // Backwards compatibility + PeerHandler *m_bc_peerhandler; + int m_bc_receive_timeout; + + void SetPeerID(u16 id){ m_peer_id = id; } + u32 GetProtocolID(){ return m_protocol_id; } void PrintInfo(std::ostream &out); void PrintInfo(); + std::string getDesc(); u16 m_indentation; - -private: - u32 m_protocol_id; - float m_timeout; - PeerHandler *m_peerhandler; - core::map m_peers; - u16 m_peer_id; - //bool m_waiting_new_peer_id; - u32 m_max_packet_size; - UDPSocket m_socket; }; } // namespace diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 586eaa2..7f0d46a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -100,7 +100,7 @@ void set_default_settings(Settings *settings) //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "2"); - settings->setDefault("max_simultaneous_block_sends_server_total", "2"); + settings->setDefault("max_simultaneous_block_sends_server_total", "8"); settings->setDefault("max_block_send_distance", "7"); settings->setDefault("max_block_generate_distance", "5"); settings->setDefault("time_send_interval", "20"); diff --git a/src/main.cpp b/src/main.cpp index df1347f..bc44775 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -481,6 +481,8 @@ MainGameCallback *g_gamecallback = NULL; // Connection std::ostream *dout_con_ptr = &dummyout; std::ostream *derr_con_ptr = &verbosestream; +//std::ostream *dout_con_ptr = &infostream; +//std::ostream *derr_con_ptr = &errorstream; // Server std::ostream *dout_server_ptr = &infostream; diff --git a/src/map.cpp b/src/map.cpp index ba4130c..8aad4e5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -21,7 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapsector.h" #include "mapblock.h" #include "main.h" +#ifndef SERVER #include "client.h" +#endif #include "filesys.h" #include "utility.h" #include "voxel.h" diff --git a/src/server.cpp b/src/server.cpp index 1a441e8..37ba65a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1074,7 +1074,7 @@ void Server::start(unsigned short port) m_thread.stop(); // Initialize connection - m_con.setTimeoutMs(30); + m_con.SetTimeoutMs(30); m_con.Serve(port); // Start thread @@ -1823,9 +1823,18 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - con::Peer *peer; try{ - peer = m_con.GetPeer(peer_id); + Address address = m_con.GetPeerAddress(peer_id); + + // drop player if is ip is banned + if(m_banmanager.isIpBanned(address.serializeString())){ + SendAccessDenied(m_con, peer_id, + L"Your ip is banned. Banned name was " + +narrow_to_wide(m_banmanager.getBanName( + address.serializeString()))); + m_con.DeletePeer(peer_id); + return; + } } catch(con::PeerNotFoundException &e) { @@ -1834,17 +1843,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - // drop player if is ip is banned - if(m_banmanager.isIpBanned(peer->address.serializeString())){ - SendAccessDenied(m_con, peer_id, - L"Your ip is banned. Banned name was " - +narrow_to_wide(m_banmanager.getBanName( - peer->address.serializeString()))); - m_con.deletePeer(peer_id, false); - return; - } - - u8 peer_ser_ver = getClient(peer->id)->serialization_version; + u8 peer_ser_ver = getClient(peer_id)->serialization_version; try { @@ -1865,7 +1864,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; infostream<<"Server: Got TOSERVER_INIT from " - <id<serialization_version = deployed; - getClient(peer->id)->pending_serialization_version = deployed; + getClient(peer_id)->pending_serialization_version = deployed; if(deployed == SER_FMT_VER_INVALID) { @@ -1900,7 +1899,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]); } - getClient(peer->id)->net_proto_version = net_proto_version; + getClient(peer_id)->net_proto_version = net_proto_version; if(net_proto_version == 0) { @@ -2045,11 +2044,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT2) { infostream<<"Server: Got TOSERVER_INIT2 from " - <id<id)->serialization_version - = getClient(peer->id)->pending_serialization_version; + getClient(peer_id)->serialization_version + = getClient(peer_id)->pending_serialization_version; /* Send some initialization data @@ -2059,8 +2058,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerInfos(); // Send inventory to player - UpdateCrafting(peer->id); - SendInventory(peer->id); + UpdateCrafting(peer_id); + SendInventory(peer_id); // Send player items to all players SendPlayerItems(); @@ -2074,7 +2073,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) { SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( m_env.getTimeOfDay()); - m_con.Send(peer->id, 0, data, true); + m_con.Send(peer_id, 0, data, true); } // Send information about server to player in chat @@ -2095,7 +2094,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Warnings about protocol version can be issued here - if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION) + if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION) { SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER"); } @@ -2402,7 +2401,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else if(action == 2) { #if 0 - RemoteClient *client = getClient(peer->id); + RemoteClient *client = getClient(peer_id); JMutexAutoLock digmutex(client->m_dig_mutex); client->m_dig_tool_item = -1; #endif @@ -2685,7 +2684,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } // Reset build time counter - getClient(peer->id)->m_time_from_building = 0.0; + getClient(peer_id)->m_time_from_building = 0.0; // Create node data MaterialItem *mitem = (MaterialItem*)item; @@ -3428,11 +3427,10 @@ core::list Server::getPlayerInfo() Player *player = *i; try{ - con::Peer *peer = m_con.GetPeer(player->peer_id); - // Copy info from peer to info struct - info.id = peer->id; - info.address = peer->address; - info.avg_rtt = peer->avg_rtt; + // Copy info from connection to info struct + info.id = player->peer_id; + info.address = m_con.GetPeerAddress(player->peer_id); + info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); } catch(con::PeerNotFoundException &e) { diff --git a/src/server.h b/src/server.h index dac7e28..b238bec 100644 --- a/src/server.h +++ b/src/server.h @@ -468,9 +468,9 @@ public: return m_banmanager.getBanDescription(ip_or_name); } - con::Peer* getPeerNoEx(u16 peer_id) + Address getPeerAddress(u16 peer_id) { - return m_con.GetPeerNoEx(peer_id); + return m_con.GetPeerAddress(peer_id); } // Envlock and conlock should be locked when calling this diff --git a/src/servercommand.cpp b/src/servercommand.cpp index a090039..1565989 100644 --- a/src/servercommand.cpp +++ b/src/servercommand.cpp @@ -250,20 +250,19 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx) os<server->getPeerAddress(player->peer_id); + std::string ip_string = address.serializeString(); + ctx->server->setIpBanned(ip_string, player->getName()); + os<getName()); - con::Peer *peer = ctx->server->getPeerNoEx(player->peer_id); - if(peer == NULL) - { + actionstream<player->getName()<<" bans " + <getName()<<" / "<address.serializeString(); - ctx->server->setIpBanned(ip_string, player->getName()); - os<getName()); - - actionstream<player->getName()<<" bans " - <getName()<<" / "<address; + server.GetPeerAddress(peer_id_client); infostream<<"*** Sending packets in wrong order (2,1,2)" <channels[chn]; + con::Channel *ch = &server.getPeer(peer_id_client)->channels[chn]; u16 sn = ch->next_outgoing_seqnum; ch->next_outgoing_seqnum = sn+1; server.Send(peer_id_client, chn, data2, true); @@ -1004,6 +1026,7 @@ struct TestConnection } assert(got_exception); } +#endif { const int datasize = 30000; SharedBuffer data1(datasize); @@ -1022,12 +1045,25 @@ struct TestConnection server.Send(peer_id_client, 0, data1, true); - sleep_ms(50); + sleep_ms(3000); u8 recvdata[datasize + 1000]; infostream<<"** running client.Receive()"< 5000) + break; + try{ + size = client.Receive(peer_id, recvdata, datasize + 1000); + received = true; + }catch(con::NoIncomingDataException &e){ + } + sleep_ms(10); + } + assert(received); infostream<<"** Client received: peer_id="< Date: Fri, 21 Oct 2011 18:37:43 +0300 Subject: [PATCH 40/40] Make it to compile on MSVC2010 --- src/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connection.cpp b/src/connection.cpp index d941f36..cdf8cd3 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1527,7 +1527,7 @@ ConnectionEvent Connection::waitEvent(u32 timeout_ms) { try{ return m_event_queue.pop_front(timeout_ms); - } catch(ItemNotFoundException &e){ + } catch(ItemNotFoundException &ex){ ConnectionEvent e; e.type = CONNEVENT_NONE; return e;