[network] 服务器编程

server::run()一些注释

1、network::accept_connect是非阻塞等待。

2、string_ip:本机连接时是127.0.0.1。

3、reason:空。指示accept_connect过程没有发生错误。

4、sock:为此个客户端服务的sock。

 

处理登陆

登陆分两个步骤,分别对应两个包:(process_login处理这两个包)

1、[version]version="0.1.4"[/version]

2、[login]selective_ping="1"username="ancientcc2"[/login]

 

处理游戏大厅

游戏大厅触发的动作:(process_data_lobby处理这些个动作)

创建:[create_game]name="ancientcc2xxxxx"[/create_game]

第二个消息

"#textdomain wesnoth-multiplayer

description=_ "These caves were once the lair of Chak'kso Ney'yks, a legendary Elder Basilisk. The petrified forms of his victims remain as monuments to his savage power."

experience_modifier="70"

id="multiplayer_Basilisk"

map_data="border_size=1

usage=map

 

_off^_usr   , _off^_usr   , _off^_usr   , _off^_usr   , _off^_usr   , _off^_usr   , _off^_usr   , char *

 

加入:[join]id="1"observe="no"[/join]

<要加入的是ancientcc>

第二个消息:

[scenario_diff]

[change_child]

index="1"

[side]

[insert]

current_player="ancientcc"

name="ancientcc"

player_id="ancientcc2"

save_id="ancientcc2"

user_description="ancientcc"

[/insert]

[delete]

id="x"

[/delete]

[/side]

[/change_child]

[/scenario_diff]

第三个消息

[change_faction]

faction="0"

gender="null"

leader="random"

name="ancientcc"

[/change_faction]

 

观察:[join]id="3"observe="yes"[/join]

<要加入的是ancientcc1>

 

一、网络数据的压缩和解压缩

玩家发来命令,服务器转发向玩家命令,服务器产生事件,都是以压缩过形式在网络上流通。document封装一条命令/事件,内部操作支持了解缩和解压缩。注:这个一条是相对的,它可能是数条命令合成的一条命令,像send_history后形成的document。

要知道压缩和解压缩大概是怎么个过程首先就要了解document对象几个成员,它大概存储了什么内容。

@compressed_buf:已压缩过数据。这往往是直接从网络上收到的数据。这个构造函数作就是根据网络上收到数据恢复出命令。
document::document(string_span compressed_buf) :
	compressed_buf_(compressed_buf),
	output_(NULL),
	buffers_(),
	root_(NULL),
	prev_(NULL),
	next_(NULL)
{
	string_span uncompressed_buf;
	// uncompress_buffer执行解压缩,解压缩后的数据放在从堆中分配出的一块内存
	buffers_.push_back(uncompress_buffer(compressed_buf, &uncompressed_buf));
	output_ = uncompressed_buf.begin();
	const char* cbuf = output_;
	try {
		root_ = new node(*this, NULL, &cbuf);
	} catch(...) {
		delete [] buffers_.front();
		buffers_.clear();
		throw;
	}

	attach_list();
}
至此大概可以总结下各个成员意义
compressed_buf_:压缩过数据。
buffers_:数据存储缓冲。它是一个vector,它的[0]存储的是未压缩过数据,[1]存储的是压缩过数据。注意下这两个数据都被从堆中分配,析构时要delete。
output_:未压缩过数据。
root_:<尚未理解>

程序往往要遍历历史命令,这个遍历是通过把命令组织在一个链表,attach_list()作用就这是把这个命令放入链表。下图是使用的链表结构

send_history会遍历所有历史命令(但它不是通过链表),并在遍历时把所有命令合成一条命令,形成一个新的document

功能:把历史记录发向sock指定玩家。
注:在发送过程中,它会把原有记录合成一条记录,再写回历史容器
void game::send_history(const network::connection sock) const
{
	if(history_.empty()) {
		return;
	}

	//we make a new document based on converting to plain text and
	//concatenating the buffers.
	//TODO: Work out how to concentate buffers without decompressing.
	std::string buf;
	for(std::vector<simple_wml::document*>::iterator i = history_.begin();
	    i != history_.end(); ++i) {
		// (*i)->output()返回是未压缩过数据
		buf += (*i)->output();
		delete *i;
	}

	try {
		simple_wml::document* doc = new simple_wml::document(buf.c_str(), simple_wml::INIT_STATIC);
		const simple_wml::string_span& data = doc->output_compressed();
		// 压缩
		doc->compress();
		network::send_raw_data(data.begin(), data.size(), sock,"game_history");
		history_.clear();
		history_.push_back(doc);
	} catch (simple_wml::error& e) {
		WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
	}
}

 

二、FAQ

A:如何让0.1.6的客户端登陆进1.9.x的服务器?

Q:修改multiplayer.cpp中的open_connection(...),

if(data.child("version")) {
	config cfg;
	config res;
	cfg["version"] = game_config::version;
	res.add_child("version", cfg);
	network::send_data(res, 0, true);
}
cfg["version"] = game_config::version改为cfg["version"] = "0.1.5";

 

A:登陆后,如何让0.1.6的客户端使用1.9.x的服务器?

Q:使用后要做到:

1、kingdom客户端不能“加入”、“观察”wesnoth创建游戏。

2、wesnoth客户端不能“加入”、“观察”kingdom创建游戏。

实现方法:

创建游戏时会发送[create_game]块,[create_game]中除了name字段可以有password。让kingdom创建游戏时使用一个固定密码(会使用checksum),客户端要加入需要这个密码。kingdom客户端会自动给出这个密码,wesnoth客户端则要求这个密码,但不知道这个密码无法“加入”、“观察”这个游戏。

改了era,让wesnoth客户端都认为是“未知时代”,以灰掉“加入”、“观察”按钮。

 

A:创建的游戏中含有较验码,避免客户端配置文件不一致的玩同一个剧本?

Q:

 

三、对韦诺之战服务器代码做的修改

服务器尽量会做到和韦诺之战服务器通用。能不改的尽量不改。

server::read_config()


当中要读服务器配置,这个配置往往被放在My Documents目录下,而在Windows下这个目录往往带中文,这时需要能解析中文目录名的读文件函数。

scoped_istream stream = preprocess_file(config_file_);
改为
scoped_istream stream = istream_file(config_file_, true);

对于“建立联网游戏”,它是启动游戏时自动启动服务器,这时要读取的配置文件往往是放在My Documents目录下。

全部评论: 0

    写评论: