EQEmulator Forums

EQEmulator Forums (https://www.eqemulator.org/forums/index.php)
-   Development::Feature Requests (https://www.eqemulator.org/forums/forumdisplay.php?f=612)
-   -   New Concept: Proximity Spawning (to reduce bandwidth usage) (https://www.eqemulator.org/forums/showthread.php?t=26389)

thepoetwarrior 12-09-2012 09:31 AM

Any further work on these ideas?

Drajor 12-10-2012 08:31 PM

I have tentative plans to implement something similar on my server. I have not read this entire thread but from the gist I got my approach would be very different. There are already well established techniques and algorithms for dividing and searching space. See quadtree or octtree. If I do have a go I will share the results, whether successful or not.

Drajor 12-11-2012 04:21 AM

I got inspired and started work on this. My theory is as follows, note that I do not know anything about the mob update packets yet so I have made some assumptions.

This approach aims to address two things;
- Improve NPC proximity aggro check performance.
- Add the concept of Mob 'visibility'.

Each (zone)instance has a quadtree which all Mob's are added to when they are created and removed from when they are destroyed. The quadtree provides a query(region) interface which returns a list of Mob* which are within this region.

-- Aggro
When it comes time for Mobs to check for proximity agro, each mob passes an AABB which encloses it's aggro circle to the query interface. From the Mobs returned, we compare the absolute distance and factions as per normal.

-- Visibility
The client will query() at a fixed interval and provide an AABB which encloses it's 'visibility circle'. From the list of Mobs returned we check if any were added or removed (list diff, twice). If a Mob was added, the client gets a SPAWN_PACKET or if removed, DESPAWN_PACKET. When it comes time to update the client with new Mob positions, we only send packets for the mobs on their visibility list.

-- Important Notes
- Each time a Mob (Client or NPC) moves, they will need to update their position in the quadtree.
- Just before a mob checks for proximity aggro, they will need to update their AABB.

Comments? Suggestions?

Drajor 12-11-2012 05:24 AM

Upon further inspection I have decided to not continue with this. I believe the number of changes required would be significant and I do not have the knowledge to undertake them. Below is my (untested) code for the quadtree.

Code:

#ifndef DG_QUAD_TREE
#define DG_QUAD_TREE

#define QTCHILDREN 4
#define QTCAPACITY 25

// A maximum tree depth is defined to prevent infinite subdivisions (consider a train of 50 mobs all sitting on top of each other).
#define QTMAXDEPTH 10

// Directions.
#define QTNE 0
#define QTNW 1
#define QTSE 2
#define QTSW 3

#include <list>

class Mob;

struct QTAABB {
        QTAABB(float pX, float pY, float pHalfDim);
        float mX;
        float mY;
        float mHalfDim;
        float mExtents[4];

        const bool contains(Mob* pMob);
        const bool intersects(QTAABB& pRegion);
};

class QuadTree {
public:

        static QuadTree* create(float pX, float pY, float pHalfDim);
        ~QuadTree();

        __forceinline const bool isLeaf() const { return mChildren[QTNE] == 0; }

        const bool insert(Mob* pMob);
        void remove(Mob* pMob);

        // Search!
        void query(QTAABB& pRegion, std::list<Mob*>& pResult);

private:

        QuadTree(QuadTree* pParent, float pX, float pY, float pHalfDim, unsigned short pDepth);
       
        void add(Mob* pMob);
        void subdivide();
        QTAABB mRegion;
        QuadTree* mParent;
        QuadTree* mChildren[QTCHILDREN];
        unsigned short mDepth;

        typedef std::list<Mob*> MobList;
        typedef MobList::iterator MobListIterator;
        MobList mMobs;
};

#endif

Code:

#include "dg_quadtree.h"
#include "../zone/mob.h"

#define QTMINX 0
#define QTMAXX 1
#define QTMINY 2
#define QTMAXY 3

// Shorthand Extents.
#define AABB_MINX mExtents[QTMINX]
#define AABB_MAXX mExtents[QTMAXX]
#define AABB_MINY mExtents[QTMINY]
#define AABB_MAXY mExtents[QTMAXY]

QTAABB::QTAABB(float pX, float pY, float pHalfDim) : mX(pX), mY(pY), mHalfDim(pHalfDim) {
        // Pre-calculate AABB extents.
        AABB_MINX = mX - mHalfDim;
        AABB_MAXX = mX + mHalfDim;
        AABB_MINY = mY - mHalfDim;
        AABB_MAXY = mY + mHalfDim;
}

const bool QTAABB::contains(Mob* pMob) {
        float mobX = pMob->GetX();
        float mobY = pMob->GetY();
        return mobX >= AABB_MINX && mobX < AABB_MAXX && mobY >= AABB_MINY && mobY < AABB_MAXY;
}

const bool QTAABB::intersects(QTAABB& pRegion) {
        const float dims = pRegion.mHalfDim + mHalfDim;
        return fabs(pRegion.mX - mX) <= dims && fabs(pRegion.mY - mY) <= dims;
}

QuadTree* QuadTree::create(float pX, float pY, float pHalfDim) {
        return new QuadTree(0, pX, pY, pHalfDim, 0);
}

QuadTree::QuadTree(QuadTree* pParent, float pX, float pY, float pHalfDim, unsigned short pDepth) : mParent(pParent), mRegion(pX, pY, pHalfDim), mDepth(pDepth) {
        memset(mChildren, 0, sizeof(QuadTree*)*QTCHILDREN);
}

QuadTree::~QuadTree() {
        if(!isLeaf())
                for(int i = 0; i < QTCHILDREN; i++)
                        delete mChildren[i];
}

void QuadTree::query(QTAABB& pRegion, std::list<Mob*>& pResult) {

        if(!mRegion.intersects(pRegion)) return;

        // Check: Each mob that belongs to this region.
        for(MobListIterator i = mMobs.begin(); i != mMobs.end(); i++)
                if(pRegion.contains(*i))
                        pResult.push_back(*i);

        // No children to check.
        if(isLeaf()) return;

        // Search continues.
        for(int i = 0; i < QTCHILDREN; i++)
                mChildren[i]->query(pRegion, pResult);
}

void QuadTree::add(Mob* pMob) {
        mMobs.push_back(pMob);
}

const bool QuadTree::insert(Mob* pMob) {
        // Check: pMob within this region.
        if(!mRegion.contains(pMob)) return false;

        const bool hasCapacity = mMobs.size() < QTCAPACITY;
        const bool atMaxDepth = mDepth == QTMAXDEPTH;

        // Check: Is there capacity for pMob in this region OR the tree has reached the maximum depth allowed.
        if(hasCapacity || atMaxDepth) {
                add(pMob);
                return true;
        }
        else if(isLeaf()) {
                // Divide this region.
                subdivide();

                // Try adding pMob to the new children.
                for(int i = 0; i < QTCHILDREN; i++)
                        if (mChildren[i]->insert(pMob))
                                return true;
        }

        // Error.
        return false;
}

void QuadTree::remove(Mob* pMob) {
        mMobs.remove(pMob);
}

void QuadTree::subdivide() {
        const unsigned short childDepth = mDepth+1;
        const float quarterDim = mRegion.mHalfDim * 0.5f;
        mChildren[QTNE] = new QuadTree(this, mRegion.mX + quarterDim, mRegion.mY + quarterDim, quarterDim, childDepth);
        mChildren[QTNW] = new QuadTree(this, mRegion.mX - quarterDim, mRegion.mY + quarterDim, quarterDim, childDepth);
        mChildren[QTSE] = new QuadTree(this, mRegion.mX + quarterDim, mRegion.mY - quarterDim, quarterDim, childDepth);
        mChildren[QTSW] = new QuadTree(this, mRegion.mX - quarterDim, mRegion.mY - quarterDim, quarterDim, childDepth);
}



All times are GMT -4. The time now is 12:19 AM.

Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.