minetest源码解析七:Client端更新流程

minetest源码解析七:Client端更新流程

客户端更新主要使用的函数是void Client::step(float dtime)

minetest->client.cpp


这个函数作用是更新客户端,客户端主动去接收服务器端发过来的信息,然后对环境等进行更新。

使用范围:都在the_game()这个函数中(minetest->game.cpp->the_game())。这个函数中共有3处使用。


(1)wait for server to accept connection 时使用,循环执行client.step()来更新客户端,这个时候游戏界面还没有出来之前,直到客户端接收到 TOCLIENT_INIT命令,从而根据命令执行更新m_state = LC_Init才退出循环。

(2)Wait until content has been received时使用,循环执行client.step()来更新客户端,这个时候游戏界面还没有出来之前,直到客户端陆续接收到TOCLIENT_ITEMDEFTOCLIENT_NODEDEF命令,代表内容数据接收完毕之后才退出循环。

(3)Run server, client 时使用,这个是在game循环中,不断循环执行更新客户端。



客户端更新流程图(Client::step)



流程图中核心函数说明



1.ReceiveAll()   

      接收数据命令,服务端发过来的,接收到的命令主要是这个形式ToClientCommand,服务端发过来的命令存在Connection对象的MutexedQueue<ConnectionEvent> m_event_queue中。


涉及到的函数

void Client::ReceiveAll()

{

for(;;)

{

if(porting::getTimeMs() > start_ms + 100)

break;

Receive();

。。。

}

}


void Client::Receive()

{

DSTACK(__FUNCTION_NAME);

SharedBuffer<u8> data;

u16 sender_peer_id;

u32 datasize = m_con.Receive(sender_peer_id, data);

ProcessData(*data, datasize, sender_peer_id);

}


m_con.Receive(sender_peer_id, data);

minetest->connection.cpp

u32 Connection::Receive(u16 &peer_id, SharedBuffer<u8> &data)

{

for(;;)

{

ConnectionEvent e = waitEvent(m_bc_receive_timeout);

switch(e.type)

{

case CONNEVENT_NONE:

throw ;

case CONNEVENT_DATA_RECEIVED:

return ;

case CONNEVENT_PEER_ADDED

continue

case CONNEVENT_PEER_REMOVED

continue

case CONNEVENT_BIND_FAILED:

throw  ;

}

}

}


minetest->connection.h

MutexedQueue<ConnectionEvent> m_event_queue;

enum ConnectionEventType{

CONNEVENT_NONE,

CONNEVENT_DATA_RECEIVED,

CONNEVENT_PEER_ADDED,

CONNEVENT_PEER_REMOVED,

CONNEVENT_BIND_FAILED,

};



void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)

minetest->client.cpp

      这个实际对接收到的数据,主要根据接收数据中的ToClientCommand命令进行处理,有些命令直接处理,有些直接加入到事件列表中,后续再处理,返回到the_game()函数中的循环体中,主要处理时在game之后的流程中,// Read client events会对clientEvent进判断并处理。


ToClientCommand command = (ToClientCommand)readU16(&data[0]);

u8 ser_version = m_server_ser_ver;

if(command == TOCLIENT_INIT)

{ …

writeU16(&reply[0], TOSERVER_INIT2);

m_con.Send(PEER_ID_SERVER, 1, reply, true);

m_state = LC_Init;

return;

}

if(command == TOCLIENT_ACCESS_DENIED)

{

return;

}

if(ser_version == SER_FMT_VER_INVALID)

return;

/*

  Handle runtime commands  (运行时命令)

*/


if(command == TOCLIENT_REMOVENODE)

else if(command == TOCLIENT_ADDNODE)

else if(command == TOCLIENT_BLOCKDATA)

else if(command == TOCLIENT_INVENTORY)

else if(command == TOCLIENT_TIME_OF_DAY)

。。。



2.Packet counter

计数器



3.初始client startup 发送TOSERVER_INIT 

minetest->clientserver.h   ToServerCommand  ToClientCommand

client创建之后第二次更新的时候。(这里是第二次,做了一个延时)

初次更新客户端时,还没有连接,程序执行到这就退出了。


涉及到的函数    


Send(1, data, false);

void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)

{

m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);

}

minetest->connection.cpp



4.Run Map's timers and unload unused data

运行Map的定时器,获取deleted_blocks,并通知服务器端。


涉及到的函数

m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), &deleted_blocks);

m_con.Send(PEER_ID_SERVER, 2, reply, true);

writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);



5.Handle environment

处理环境相关的。


涉及到的函数

minetest->environment.cpp

m_env.step(dtime);主要更新光照、溺水、水流淌速度更新、熔岩、activeObject StepClientSimpleObject() step


Get events

for(;;)

{

ClientEnvEvent event = m_env.getClientEvent();

if(event.type == CEE_NONE)   //Client环境中的客户端事件列表为空时

break;

else if(event.type == CEE_PLAYER_DAMAGE)

else if(event.type == CEE_PLAYER_BREATH)

}


ClientEnvEvent 一个结构体

minetest->environment.h 

enum ClientEnvEventType

{

CEE_NONE,

CEE_PLAYER_DAMAGE,

CEE_PLAYER_BREATH

};


minetest->client.h 

enum ClientEventType

{

CE_NONE,

CE_PLAYER_DAMAGE,

CE_PLAYER_FORCE_MOVE,

CE_DEATHSCREEN,

CE_SHOW_FORMSPEC,

CE_SPAWN_PARTICLE,

CE_ADD_PARTICLESPAWNER,

CE_DELETE_PARTICLESPAWNER,

CE_HUDADD,

CE_HUDRM,

CE_HUDCHANGE,

CE_SET_SKY,

CE_OVERRIDE_DAY_NIGHT_RATIO,

};



6.Send player position to server

给服务端发送玩家位置

涉及到的函数

sendPlayerPos();

void Client::sendPlayerPos()

{

。。。

writeU16(&data[0], TOSERVER_PLAYERPOS);

。。。

}



7.Replace updated meshes

更新mesh


主要涉及到的函数

while(!m_mesh_update_thread.m_queue_out.empty())

{

MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();

MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);

if(block)

{

// Delete the old mesh

// Replace with the new mesh

block->mesh = r.mesh;

} else {

delete r.mesh;

}

if(r.ack_block_to_server)

{

//Acknowledge block

writeU16(&reply[0], TOSERVER_GOTBLOCKS);

m_con.Send(PEER_ID_SERVER, 2, reply, true);

}

}



8.Load fetched media

加载获取到的media

m_media_downloader->step(this);

if (m_media_downloader->isDone()) 

received_media();



9.更改库存 m_inventory_from_server

如果m_inventory_from_server有值,且相隔一个interval时间,更新玩家的库存。


涉及到的函数

player->inventory = *m_inventory_from_server;

m_inventory_updated = true;



10.Update positions of sounds attached to objects

更新Object的声音位置



11.Handle removed remotely initiated sounds

// Find removed sounds and clear references to them,这些的更改一般是在Client::ProcessData中进行的。

writeU16(os, TOSERVER_REMOVED_SOUNDS);




12. LocalClientState

minetest->client.h

enum LocalClientState 

{

LC_Created,

LC_Init,

LC_Ready

};

LC_Created:初始化;TOCLIENT_INITLC_Init afterContentReceived()  LC_Ready


LC_Ready 只有一个地方调用 client.afterContentReceived(device,font);

内容接收之后:ItemDefManager、NodeDefManager、Media,进入game循环之前设置进去的,也就是进入循环之后状态肯定是LC_Ready .



13.m_client_event_queue 理解 共有两处  (minetest->client.h)

Queue<ClientEvent> m_client_event_queue;


enum ClientEventType

{

CE_NONE,

CE_PLAYER_DAMAGE,

CE_PLAYER_FORCE_MOVE,

CE_DEATHSCREEN,

CE_SHOW_FORMSPEC,

CE_SPAWN_PARTICLE,

CE_ADD_PARTICLESPAWNER,

CE_DELETE_PARTICLESPAWNER,

CE_HUDADD,

CE_HUDRM,

CE_HUDCHANGE,

CE_SET_SKY,

CE_OVERRIDE_DAY_NIGHT_RATIO,

};


struct ClientEvent

{

ClientEventType type;

union{

struct{

} none;

struct{

u8 amount;

} player_damage;

struct{

f32 pitch;

f32 yaw;

} player_force_move;

struct{

bool set_camera_point_target;

f32 camera_point_target_x;

f32 camera_point_target_y;

f32 camera_point_target_z;

} deathscreen;

struct{

std::string *formspec;

std::string *formname;

} show_formspec;

struct{

} textures_updated;

struct{

v3f *pos;

v3f *vel;

v3f *acc;

f32 expirationtime;

f32 size;

bool collisiondetection;

bool vertical;

std::string *texture;

} spawn_particle;

struct{

u16 amount;

f32 spawntime;

v3f *minpos;

v3f *maxpos;

v3f *minvel;

v3f *maxvel;

v3f *minacc;

v3f *maxacc;

f32 minexptime;

f32 maxexptime;

f32 minsize;

f32 maxsize;

bool collisiondetection;

bool vertical;

std::string *texture;

u32 id;

} add_particlespawner;

struct{

u32 id;

} delete_particlespawner;

struct{

u32 id;

u8 type;

v2f *pos;

std::string *name;

v2f *scale;

std::string *text;

u32 number;

u32 item;

u32 dir;

v2f *align;

v2f *offset;

v3f *world_pos;

v2s32 * size;

} hudadd;

struct{

u32 id;

} hudrm;

struct{

u32 id;

HudElementStat stat;

v2f *v2fdata;

std::string *sdata;

u32 data;

v3f *v3fdata;

v2s32 * v2s32data;

} hudchange;

struct{

video::SColor *bgcolor;

std::string *type;

std::vector<std::string> *params;

} set_sky;

struct{

bool do_override;

float ratio_f;

} override_day_night_ratio;

};

};


使用

ClientEvent Client::getClientEvent()

{

return m_client_event_queue.pop_front();

}


the_game() 循环体中循环执行客户端事件

{

for(;;)

{

// Read client events

for(;;)

{

ClientEvent event = client.getClientEvent();

}

}

}



14.m_client_event_queue 理解 共有两处  (minetest->environment.h)

std::list<ClientEnvEvent> m_client_event_queue;


enum ClientEnvEventType

{

CEE_NONE,

CEE_PLAYER_DAMAGE,

CEE_PLAYER_BREATH

};


struct ClientEnvEvent

{

ClientEnvEventType type;

union {

struct{

} none;

struct{

u8 amount;

bool send_to_server;

} player_damage;

struct{

u16 amount;

} player_breath;

};

};


Client->step()中使用,在Handle environment时循环执行完所有的环境event队列事件。

for(;;)

{

ClientEnvEvent event = m_env.getClientEvent();

if(event.type == CEE_NONE) break;

else if(event.type == CEE_PLAYER_DAMAGE)

else if(event.type == CEE_PLAYER_BREATH)

。。。

}

客户端environment的事件都是在environment的step中设置进去的。只有client的step中才会执行到environment的step。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值