sudo apt-get update
sudo apt-get install git clang cmake gcc g++
libmysqlclient-dev libss-dev libbz2-dev libreadline-dev
libncurses-dev libboost-all-dev mysql-server-5.7 p7zip
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang 100
//
sudo yum install -y clang
git clone -b 3.3.5 https//github.com//TrinityCore/TrinityCore.git
mkdir build cd build
cmake ../ -DCMAKE_INSTALL_PREFIX=/home/lighthouse/tinycore
-DCONF_DIR=/home/lighthouse/tinycore/bin
make -j2 (nproc看核心数来编译)
make install
1)cd ~
2)mkdir res (根目录创建res文件夹)
3)把客户端目录的Data和Interface移动到res目录下
4)cd res
在res目录执行游戏目录bin/下面的mapextractor,
生成dbc和maps文件夹
5)mkdir vmaps
在res目录执行游戏目录bin/下面的vmap4extractor,
生成vmaps文件夹和Buildings目录
5)在res目录执行游戏目录bin/下面的vmap4assembler,
../lighthouse/bin/vmap4assembler Buildings vmaps
6) mkdir mmaps
在res目录执行游戏目录bin/下面的mmaps_generator
生成mmaps目录
mysql -uroot -p password
1)在TrinityCore的sql/create目录的路径赋值,打开mysql
mysql> source ../Trinity/sql/create/create_mysql.sql
show database;
可以看到生成了auth \ character \ world
1)先进到项目的bin目录,复制authserver.conf
启动授权服务器
./authserver
2)同样步骤,复制worldserver.conf
并且改写DataDir,指定res目录
DataDir="../../res"
启动worldserver
启动授权服务器
./worldserver
①哪些模块会用到地图模块?
1)运动模块:走、跳、飞行
2)副本
3)寻路
②地图模块要实现哪些功能?
1)AOI:管理地图地理信息、地图对象信息
2)功能:视野、数据同步、碰撞检测、寻路算法
③怎么驱动地图模块?
1)移动的网络数据驱动
2)定时更新(怪物的AI行为)
职责:
①静态数据:
《1》trinity由mapextractor生成.map文件(基础地图信息),数据包括
1) area data (区域物体信息)
2) height data (高度信息)
3) liquid data (水)
4) hole data (洞)
《2》由mmaps_generator生成可移动地图信息 .mmap,游戏中的地图移动数据(用来给navmesh寻路),也就是用recast和detour生成寻路信息
1) mmtile 索引对应具体地图的所有信息(x+y做名字前缀)
2) .mmap 索引
《3》vmapsextractor生成地图元素信息(可视场景信息:山脉、水体、建筑物等静态场景信息),用于未来做碰撞检测,
*.m2和 *.wmo文件 静态物品包围盒信息
*.mdx 角色、物品和怪物包围盒信息
《4》vmap4assembler合并vmapsextractor和mmaps_generator生成的地图信息,vmtile信息
②动态数据
NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
//NGridType是动态数据
GridMap* GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; //原始数据,最新的已去除
//静态数据,64*64的格子,
//每个格子是8*8个cell,grid和cell都有自己的坐标系
//动物、角色、怪物都是在具体的cell当中
std::bitset marked_cells;
玩家在哪个grid的cell,哪个grid才会被加载,否则不会被加载到内存当中 (只有玩家进入这个区域才会动态加载这个区域的资源)
①数据结构
//NGridType是模板类
extern template class NGrid;
typedef NGrid NGridType;
AllWorldObjectTypes:(动态的obj)
player玩家、宠物、尸体、动态物品(远处target)
typedef TYPELIST_4(Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/)
AllWorldObjectTypes;
AllGridObjectTypes :(静态的obj)
游戏obj、除了宠物的obj、动态物体、尸体
typedef TYPELIST_7(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation) AllGridObjectTypes;
②具体分析
NGrid有两个容器:i_container和i_objects
private:
uint32 i_gridId;
GridInfo i_GridInfo;
GridReference > i_Reference;
int32 i_x;
int32 i_y;
grid_state_t i_cellstate;
GridType i_cells[N][N];
bool i_GridObjectDataLoaded;
typedef Grid GridType;
class Grid
private:
TypeMapContainer i_container;
TypeMapContainer i_objects;
玩家进入某个grid的cell,地图加载数据激活推动消息推送
MapRefManager m_mapRefManager;//MapRefManager双向循环链表
1)根据地图ID、xyz轴、面向在地图上准备new obj
2)把玩家链接到这个地图对应的双向循环链表上
3)根据玩家坐标创建一个cell,加载玩家到对应地图的内存上,若这个cell只有一个玩家则加载cell全部地图信息
4)把玩家的包围盒和怪物、NPC、玩家用BSP tree包围盒做碰撞检测(访问者模式)
DynamicMapTree _dynamicTree;
void Balance() { _dynamicTree.balance(); }
1)当定时器超时玩家没重连,那就把玩家从当前地图清除
⑤地图的状态
1)Idle
2)Active
3)InValid //无效状态
4)REMOVAL //准备从内存中卸载
typedef enum
{
GRID_STATE_INVALID = 0,
GRID_STATE_ACTIVE = 1,
GRID_STATE_IDLE = 2,
GRID_STATE_REMOVAL= 3,
MAX_GRID_STATE = 4
} grid_state_t;
玩家进入某个grid的cell,地图加载数据激活推动消息推送

1)信号处理
2)远程连接请求
3)数据库心跳
4)生成core检查
5)异步日志
...
②主线程有主循环,按照一定频率update游戏世界逻辑,执行的逻辑比如
1)更新玩家session信息,根据玩家连接状态更改session状态
(如果不是线程安全,那就在主线程处理,后面的业务逻辑是多线程处理的)
2)主线程处理的逻辑包括:从数据库获取基础的玩家初始数据比如背包等
3)其他线程中的处理:在地图中攻击、寻路、走路
③线程池B,主要处理地图数据
读取配置中的线程数量,创建线程池,并给每条线程发更新地图的放到阻塞的任务队列,每条map也能处理来自客户端发送的数据包
void MapManager::Update(uint32 diff)
{
i_timer.Update(diff);
if (!i_timer.Passed())
return;
MapMapType::iterator iter = i_maps.begin();
while (iter != i_maps.end())
{
if (iter->second->CanUnload(diff))
{
if (DestroyMap(iter->second))
iter = i_maps.erase(iter);
else
++iter;
continue;
}
if (m_updater.activated())
m_updater.schedule_update(*iter->second, uint32(i_timer.GetCurrent()));
else
iter->second->Update(uint32(i_timer.GetCurrent()));
++iter;
}
if (m_updater.activated())
m_updater.wait();
for (iter = i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->DelayedUpdate(uint32(i_timer.GetCurrent()));
i_timer.SetCurrent(0);
}
④线程池C用来负载均衡处理客户端发包(Network)
①更新这个地图中的玩家,用访问者模式给玩家广播周围的obj(让自己在同cell里面被其他人看到),处理视野相关内容
②包围盒_dynamicTree二叉树检测物体碰撞(惰性更新,每次更新的时候进行平衡)
void Map::Update(uint32 t_diff)
{
_dynamicTree.update(t_diff); //包围盒检测
/// update worldsessions for existing players
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
{
Player* player = m_mapRefIter->GetSource();
if (player && player->IsInWorld())
{
//player->Update(t_diff);
WorldSession* session = player->GetSession();
MapSessionFilter updater(session);
session->Update(t_diff, updater);
}
}
...
}
③处理人物的重生计时
/// process any due respawns
if (_respawnCheckTimer <= t_diff)
{
ProcessRespawns();
UpdateSpawnGroupConditions();
_respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS);
}
else
_respawnCheckTimer -= t_diff;
④玩家战斗中技能释放、副本流程、人物、成就的更新
1)TypeContainer.h
有两种结构:双向链表和散列表,解决不同链表、不同散列表的差异
2)TypeContainerFunctions.h
解决双向链表和散列表之间的差异
3)TypeCOntainerVistor.h
解决对访问者的抽象,不同访问者实现不同的算法
template<class T, class CONTAINER>
inline void Map::Visit(Cell const& cell, TypeContainerVisitor<T, CONTAINER>& visitor)
{
const uint32 x = cell.GridX();
const uint32 y = cell.GridY();
const uint32 cell_x = cell.CellX();
const uint32 cell_y = cell.CellY();
if (!cell.NoCreate() || IsGridLoaded(GridCoord(x, y)))
{
EnsureGridLoaded(cell);
getNGrid(x, y)->VisitGrid(cell_x, cell_y, visitor);
}
}
通过下面这个函数将不同obj加入到不同的双向链表里面
1)不同双向链表:
TypeMapContainer<GRID_OBJECT_TYPES> i_container;
TypeMapContainer<WORLD_OBJECT_TYPES> i_objects;
2)访问者算法加入不同链表的算法
template<class OBJECT>
class GridReference : public Reference<GridRefManager<OBJECT>, OBJECT>
{
protected:
void targetObjectBuildLink() override
{
// called from link()
this->getTarget()->insertFirst(this);
this->getTarget()->incSize();
}
void targetObjectDestroyLink() override
{
// called from unlink()
if (this->isValid()) this->getTarget()->decSize();
}
void sourceObjectDestroyLink() override
{
// called from invalidate()
this->getTarget()->decSize();
}
public:
GridReference() : Reference<GridRefManager<OBJECT>, OBJECT>() { }
~GridReference() { this->unlink(); }
GridReference* next() { return (GridReference*)Reference<GridRefManager<OBJECT>, OBJECT>::next(); }
};
3)不同类型的访问者做不同处理,visit就是不同的算法
class TC_GAME_API ObjectGridLoader : public ObjectGridLoaderBase
{
friend class ObjectWorldLoader;
public:
ObjectGridLoader(NGridType& grid, Map* map, Cell const& cell)
: ObjectGridLoaderBase(grid, map, cell)
{ }
void Visit(GameObjectMapType &m);
void Visit(CreatureMapType &m);
void Visit(AreaTriggerMapType &m);
void Visit(CorpseMapType &) const { } //尸体obj
void Visit(DynamicObjectMapType&) const { } //动态物品,掉落装备等
void Visit(SceneObjectMapType&) const { } //场景对象
void Visit(ConversationMapType&) const { } //对话obj
void LoadN();
};
class TC_COMMON_API DynamicMapTree
{
DynTreeImpl *impl;
public:
DynamicMapTree();
~DynamicMapTree();
bool isInLineOfSight(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos, PhaseShift const& phaseShift) const;
bool getIntersectionTime(G3D::Ray const& ray, G3D::Vector3 const& endPos, PhaseShift const& phaseShift, float& maxDist) const;
bool getObjectHitPos(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos, G3D::Vector3& resultHitPos, float modifyDist, PhaseShift const& phaseShift) const;
//看层高
float getHeight(float x, float y, float z, float maxSearchDist, PhaseShift const& phaseShift) const;
bool getAreaInfo(float x, float y, float& z, PhaseShift const& phaseShift, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const;
void getAreaAndLiquidData(float x, float y, float z, PhaseShift const& phaseShift, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const;
//添加、删除、看是否存在
void insert(GameObjectModel const&);
void remove(GameObjectModel const&);
bool contains(GameObjectModel const&) const;
//更新就是平衡
void balance();
void update(uint32 diff); //定时平衡,200ms平衡一次
};


1、200ms构建一次
2、如果BIH树发生改变才会重新构建