Error

C++博客 首页 新随笔 联系 聚合 管理
  217 Posts :: 61 Stories :: 32 Comments :: 0 Trackbacks

#

主要翻译自lua文档,加上了programming lua中自己的一些理解

require(modname)

加载给定的模块.函数首先检查表package.loaded来判定modname是否已经存在.如果存在,则require返回package.loaded[modname]所存储的值否则它尝试为模块找到一个加载器(loader).

要找到一个加载器,require首先查询package.preloaded[modname].如果它有值,该值(应该是一个函数)就是加载器.如果没值require使用package.path中存储的路径查找一个Lua的加载器.如果该查找也失败,它使用package.cpath中存储的路径查找一个C语言加载器(C loader).如果还是失败,它尝试使用all-in-one加载器(如下)

当加载一个C库的时候,require首先使用动态链接工具将应用程序与库连接起来.之后它尝试找到一个该库中的C函数,该函数要被当做加载器使用.这个C函数的名称是字符串"luaopen_"连接着复制的模块名(模块名称中的每个点号"."都被替换为一个下划线).此外,如果模块名称含有连字符"-",则第一个连字符的前缀(包括连字符)都被移除.比如,如果模块名称是a.v1-b.c,则函数名称将是luaopen_b_c.

如果require即没有为模块找到一个Lua库也没有为模块找到一个C库,他将调用all-in-one加载器.该加载器为给定模块的根名称查找C路径找到对应库(原文:this loader searches the C path for a library for the root name of the given module).例如,当require a.b.c时,它将为a查找一个库.如果找到,它查询该库内部为子模块找到一个开放函数(open function);在我们这个例子中将会是luaopen_a_b_c.使用这个便利机制(facility),一个包可以将几个子模块打包进单个的库中,同时每个子模块保存着它本来的开放函数.

一旦找到一个加载器,require使用单个的参数modname调用加载器.如果加载器返回任何值,require将其赋值给package.loaded[modname].如果加载器没有返回值且没有给package.loaded[modname]赋与任何值,则require为该条目赋值为true.无论如何,require返回package.loaded[modname]的最终值.

如果加载或者运行模块有任何错误,或者他不能为模块找到一个加载器,则require发出一个错误信号.

require函数的实现原理如下:

 

  1. --require 函数的实现  
  2. function require(name)  
  3.     if not package.loaded[name] then  
  4.         local loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示  
  5.         if loader == nil then  
  6.             error("unable to load module" .. name)  
  7.         end  
  8.         package.loaded[name] = true  
  9.         local res = loader(name)  
  10.         if res ~= nil then  
  11.             package.loaded[name] = res  
  12.         end  
  13.     end  
  14.     return package.loaded[name]  
  15. end  

 

package.cpath

由require使用查找C加载器的路径

Lua初始化C路径package.cpath的方法与初始化Lua路径package.path的相同,使用LUA_CPATH中的环境变量(另外一个默认的路径在luaconf.h中定义)

package.loaded

一个用于控制哪些模块已经加载的表,该表由require使用.当require一个模块名为modname的模块且package.loaded[modname]不为false时,require仅返回package.loaded[modname]存储的值.

package.loadlib(libname,funcname)

使用C库libname动态链接到宿主程序.在这个库中,寻找函数funcname并将该函数作为一个C函数返回.(所以,funcname必须遵守协议(参见lua_CFunction)).

这是一个底层函数.它完全绕过了package和module系统.与require不同,它不执行任何路径查找且不自动添加扩展名.libname必须是C库中完整的文件名,如果必要的话还要包含路径和扩展名.funcname必须是原封不动的C库中导出的名字(这可能取决于使用的C编译器和链接器).

这个函数不被ANSI C支持.就其本身而言,它只在一些平台上才能使用(Windows,Linux,Mac OS X,Solaris,BSD,加上其他支持dlfcn标准的Unix系统)

package.path

require用于查找Lua加载器的路径

在启动时,Lua使用环境变量LUA_PATH或者如果环境变量未定义就使用luaconf.h中定义的默认值来初始化该值.环境变量中的任何"::"都被替换为默认路径.

路径是一系列由分号隔开的模板(templates).对于每个模板,require将每个模板中的问号替换为filename,filename是modname中每个点都被替换成"目录分隔符"(比如Unix中的"/")(这句感觉翻译不准确,原文:For each template,require will change each interrogation mark in the template by filename,which is modname with each dot replaced by a "directory separator"(such as "/" in Unix));之后他将加载产生的文件名.因此,举个例子,如果Lua路径是"./?.lua;./?.lc;/usr/local/?/init.lua",为模块foo查找一个Lua加载器将会尝试以如下顺序加载文件./foo.lua,./foo.lc和/usr/local/foo/init.lua

package.preload

为特定模块存储加载器的一个表(参见require)

package.seeall(module)

为module设置一个元表,module的__index只想全局环境(global environment),以便该module继承全局环境中的值.作为函数module中的一个选项来使用.

在Programming Lua中是这么讲的:

默认情况下,module不提供外部访问.必须在调用它之前,为需要访问的外部函数或模块声明适当的局部变量.也可以通过继承来实现外部访问,只需在调用module时附加一个选项package.seeall.这个选项等价于如下代码:

 

  1. setmetatable(M,{__index = _G})  
因而只需这么做:

 

 

  1. module(...,package.seeall)  

module(name,[,...])

 

创建一个模块.如果在package.loaded[name]中有表,该表便是创建的模块.否则,如果有一个全局表t其名称与给定名称相同,则该全局表便是创建的模块.否则创建一个新的表t

posted @ 2015-04-06 16:31 Enic 阅读(299) | 评论 (0)编辑 收藏

LogServer
日志服务器看起来是一个完全独立的服务器,启动以后直接向某个逻辑服务器发起一个连接,然后根据如下的映射表工作起来:
QLOG_MSG_ENTRY( GMSG_LOG_SERVER_INFO,gmsg_log_server_info)
QLOG_MSG_ENTRY( GMSG_LOG_ITEM,gmsg_log_item)
QLOG_MSG_ENTRY( GMSG_LOG_CHAT,gmsg_log_chat)
QLOG_MSG_ENTRY( GMSG_LOG_ACCOUNT,gmsg_log_account)
QLOG_MSG_ENTRY( GMSG_LOG_SYS,gmsg_log_sys)
QLOG_MSG_ENTRY(GMSG_LOG_PLAYER,gmsg_log_player)
QLOG_MSG_ENTRY(GMSG_LOG_SAMPLE,gmsg_log_sample)
QLOG_MSG_ENTRY( GMSG_LOG_STAT,gmsg_log_stat)
QLOG_MSG_ENTRY( GMSG_LOG_GM,gmsg_log_gm)
QLOG_MSG_ENTRY(GMSG_LOG_QUEST,gmsg_log_quest)
QLOG_MSG_ENTRY(GMSG_LOG_MAIL,gmsg_log_mail)
QLOG_MSG_ENTRY(GMSG_LOG_SCRIPT,gmsg_log_script)
QLOG_MSG_ENTRY(GMSG_LOG_TENCENT_CLIENT_WG, gmsg_log_tencent_client_wg)
QLOG_MSG_ENTRY(GMSG_LOG_PING, gmsg_log_ping)
key是网络协议,value是对应的处理函数,对应的逻辑类是QLoggerHandler
QLoggerHandler管理着LogServer唯一的socket句柄,连接建立后就做一件事,把接受到的数据塞到日志数据库囧。
日志服务器协议命令码
enum EServerLogProtocol
{
GMSG_LOG_SERVER_INFO = 1,
LMSG_LOG_SERVER_CLOSE = 2,
LMSG_LOG_SERVER_ERROR = 3,
GMSG_LOG_ITEM = 4,//物品相关log
GMSG_LOG_CHAT = 5,//聊天log
GMSG_LOG_ACCOUNT = 6,//account活动log
GMSG_LOG_SYS = 7,//系统警告log
GMSG_LOG_PLAYER = 8,//玩家log
GMSG_LOG_SAMPLE = 9,//玩家log
GMSG_LOG_STAT = 10,//系统统计log
GMSG_LOG_GM = 11,//gm log
GMSG_LOG_QUEST = 12,//quest log
GMSG_LOG_MAIL = 13,//mail log
GMSG_LOG_SCRIPT = 14,//script log
GMSG_LOG_CLIENT_WG = 15,//客户端检测外挂
GMSG_LOG_TENCENT_CLIENT_WG = 16,//腾讯的检测外挂log
GMSG_LOG_PING = 17,//检测LogServer是否死掉
};
编码技巧:
1.在网络协议风暴解包中MSG_X系列宏算一个亮点。
用法举例:
msg标识一个消息数据,实际上一个ACE_Message_Block
MSG_NEW(MSG_OP_CODE, msg)  // 定义一个消息包,opcode是MSG_OP_CODE
MSG_SET_STR(  // 调用ACE_Message_Block相关接口写入需要携带的数据,数据大小变化会被ace自动感知到
MSG_DEL(smsg);  // 销毁消息
2.利用map做的消息映射表:
#define QLOG_MSG_MAP_BEGIN \
class __LOG_MSG_MAP__{ public:__LOG_MSG_MAP__(); }; \
static __LOG_MSG_MAP__  __log_msg_map__; \
__LOG_MSG_MAP__::__LOG_MSG_MAP__(){ \
QLoggerHandler::msg_map_.clear();
#define QLOG_MSG_ENTRY( QMSG, FUNC ) \
if(QLoggerHandler::msg_map_.find(QMSG)==QLoggerHandler::msg_map_.end()) \
{ QLoggerHandler::msg_map_.insert(QLoggerHandler::Msg_Map::value_type(QMSG,QLoggerHandler::FUNC));}
#define QLOG_MSG_MAP_END }
#define QLOG_MSG_ENTRY( QMSG, FUNC ) bool FUNC( ACE_Message_Block* msg );
这种设计要求网络响应函数参数是一致的,函数内部各自做解包操作。
3.统一分离出来的数据库操作:
class DBProxy
{
public:
static bool save_sys_log(const char* sys_l
static bool save_item_log(const char* serv
int left_credit,int item_guid,int item
static bool save_gm_log(int gm_log_type, c
static bool save_chat_log(int chat_type,co
static bool save_account_log( int log_type
static bool save_player_log(int log_type,c
static bool save_sample_log(int log_type,c
static bool save_stat_log(const char* serv
static bool save_quest_log(int log_type, c
static bool save_mail_log(int log_type, co
static bool save_script_log(int log_type, 
const char* log_p_2,const char* log_p_
static bool save_tencent_wg_log(const char
};
#endif//__DBProxy_H_
整个设计显得非常清晰:
QLoggerHandler接受到网络消息通过映射表static Msg_Map msg_map_;分解到各自的解包函数,然后通过DBProxy执行最终的数据库操作。
posted @ 2015-03-18 00:26 Enic 阅读(217) | 评论 (0)编辑 收藏

GatewayManager(网关管理器)
起初我以为这是一个单独的进程,专门用来管理网关,后来发现他实际上是被编译为".lib"的。
代码开起来很简单,除了协议一共三只有三个类:GatewatManager、GWClientProxy、GWSVProxy
猜测一下:
GWClientProxy表示在Gateway上的客户端
GWSVProxy表示服务器
GatewayManager做协调逻辑
简单浏览了一下代码实际应该是这样:
GatewayManager、GWClientProxy猜对了,而GWSVProxy标识的是GatewayServer。这样来看在GatewayManager的抽象中GatewayServer、Client都是独立的实体,可以通过代理做到直接访问。
整个网关的设计,应该是一个逻辑服务器做为一个GatewayManager连接并管理了多个GateWayServer来负载自己的并发连接数,同时提供了GWSVProxy和GWClientProxy来实现“透明控制”
GatewayManager类分析:
1.从GatewayManager可以获取到GWClientProxy,可以通过GUID来获取
2.GatewayManager能感知到新的客户端连接事件,事件参数包括实际连接客户端的GWSVProxy和客户端在该网关上的id,以及客户端实际连接地址
3.GatewayManager能感知到新的网关服务器连接事件
一句话描述GatewayManager的功能:
直接和网关服务器连接,管理网关服务器,透过网关服务器直接访问、管理客户端连接。
posted @ 2015-03-17 23:22 Enic 阅读(301) | 评论 (0)编辑 收藏

星座物语客户端分析---01物品编辑器
一、整体设计思路猜测
1.前期目标数据结构尽可能单一化,配置化。
2.尽可能让程序和策划的接口无人化,工具化,归纳需求以后程序提供工具给策划人员。
二、数据结构分析
所有的道具都被冗余到同一个数据结构中了。
优势:编码、和配置文件的制作上非常方便
劣势:内存略高,后期编码肯能会有负担
*所有游戏世界中任何道具都可以用_ITEM_TABLE结构体来描述
三、类型逻辑分析
目前分析到的代码,可以看出来,道具是“二级”分类,前期考虑也不够充分,后面添加的代码略显混乱。
第一级:医疗道具、装备道具、辅助道具、任务道具(道具触发任务、道具触发技能、道具触发循环任务)
第二级:
  医疗道具二级分类:HP、MP、HPMP、Fealty(宠物忠诚度)、Health
  装备道具二级分类:武器、头部、衣服、手套、鞋子、项链、戒子、肩部
  *任务道具二级分类(实际代码被卸载一级分类中了):道具触发任务、道具触发循环任务
  *辅助道具二级分类(实际代码被卸载一级分类中了):道具触发技能、ect.
四、细节分析:
1.对武器强化(打宝石,打孔)的支持不够好。
猜测_ITEM_TABLE.nNextLevel用于支持强化。实际使用可能强化前,和强化后是完全不同的两个物品,通过nNextLevel关联起来。这种设计中每个武器只要是同一类型,那么一定是统一属性的。
一个更好的方案是,给每个武器一个GUID,然后可以为武器添加全局唯一的属性,方便“小极品”,允许更多个性化的存在,同时也可以更好的追踪物品的交易流转。这样需要一个查询效率足够高的数据库来保存游戏中每一个物品的数据。目测可以用KV来解决。如果查询压力太大,可以允许数据冗余,将道具的GUID数据和持有玩家绑定起来,一个玩家ID可以批量查询出其对应的所有道具的GUID数据,直接用blob字段保存起来,同时GUID字段作为日志表保持,只在发生更改的时候才会有写操作。或者这部分数据用KV数据库系统保存。
2.有没有办法把_ITEM_TABLE结构体拆分,或者把道具做的灵活一点,变成组建系统或者属性系统。
比如:道具看成是一个组建/属性容器,放了一个装备组建进去他就具有装备的功能,放了一个医疗属性就是一个医疗物品。这样道具可以更加灵活,比如:将一个装备作为药品吃掉。(这个时候一级类型不再是类型,而是属性,具有装备道具属性同时具有道具类型属性)
3.降内存
使用protobuf代替直接使用结构体会不会好一点?_ITEM_TABLE中还使用了std::string作为字段,一个不小心memset就会挂掉。此外protobuf的优势还有支持optional等配置,可能会有优势,比如不用更具一级类型去复用二级类型的字段,而是将不同的部分独立出来作为optional字段。
五、道具属性
基础属性(三围数据):力量(Strength)、敏捷(Agility)、耐力(Stamina)、精神(Energy)、智力(Intellect)、物攻(Attack)、魔攻(Magic)、物防(Recovery)、魔防(Mrecovery)、攻速(AckSpeed)、准确(Nicety)、躲闪(Dodge)
MP、HP
体力消耗
磨损
价格
职业
ect.待续,改天直接分析策划案子,这个样子太累
posted @ 2015-03-16 17:43 Enic 阅读(296) | 评论 (1)编辑 收藏

网关服务器入网流程:
s1.连接网关管理器
s2.上报自身信息给网关管理器
s3.从网关管理器获取自己的管理信息
*s4.按照管理信息预分配好需要的资源
s5.网关服务器打开客户端监听端口
s6.网关服务器开始转发“管理器”和客户端的数据
*网关服务器完全不理解来自客户端的数据(对客户端而言是透明的)
*网关服务器和“管理器”之间存在协议栈,有五个高级命令:转发,广播转发,T下线1,T下线2,关闭网关
*网关服务器会定时给“管理器”发送心跳请求ping
*网关服务器会维护客户端状态机:Freed、Starting、InUse、Stoping
GateWayServer抽象网关逻辑以及和管理器的连接,处理管理器命令,并且转发客户端和管理器的消息
GWClient抽象网关到客户端连接,负责处理网关和客户端的逻辑
网关启动后,GateWayServer会打开监听端口,当新的连接上来以后,会创建一个新的GWClient对象,并且将新的socket连接郊游GWClient管理。GWClient管理客户端和网关服务器之间的通讯数据:接受到客户端的数据会交由GameWayServer对象转发到指定的管理器,GameWayServer也会将管理器发送给客户端的数据交由GWClient发送到客户端。
即在网关服务器内部:GWClient做为客户端的代理,GateWayServer做为管理器(逻辑服务器)的代理。
// 禁用nagles算法
int nodelay = 1;
if(ACE_OS::setsockopt(new_handle,IPPROTO_TCP, TCP_NODELAY,(char*)&nodelay,sizeof(nodelay))==-1)
{
ACE_ERROR( ( LM_ERROR, ACE_TEXT("Set socket TCP_NODELAY failed!!\n")) );
}
缺陷:
1.缺少对客户端的安全检测,也许是放到后面的逻辑服务器上去做了。不能排除恶意连接,或者误操作的连接,所有的连接都被accept上来,然后做数据交换。
2.数据的分发上,好像一个网关服务器只能对应一个管理器(逻辑服务器)
3.网关只能标识到连接(SettionID是用户逻辑无关的),无法识别到具体逻辑客户端
尝试做低级抽象:
IGateWayClientAccpetor
  OnClientAccept
IGateWay2ClientSession
  SessionID
  OnConnect
  OnRead
  OnWrite
IGateWay2ManagerSession
  SessionID
  OnConnect
  OnRead
  OnWrite
  
IGateWayDataRouter
  OnAccessRouting(IGateWay2ClientSession)
  OnAccessRouting(IGateWay2ManagerSession)
  OnGateWay2ClientSessionRead(来自客户端的数据,当前代码下,会转发给唯一的管理器处理)
  OnGateWay2ManagerSession(来自管理器的数据,如果是需要转发给客户端的数据,会包含有客户端SessionId)

协议概览:
网关管理服务器 协议:
enum EGWMgrMsgType
{
GWMMT_Msg, // Route server msg to clients.
GWMMT_MsgAll, // Route server msg to all clients.
GWMMT_Droped, // *A client has been droped by manager( actively or disconnect ack ).
GWMMT_DropedDiscard, // *A client has been droped by manager( actively or disconnect ack ).
GWMMT_Shutdown, // Shutdown gateway server.
GWMMT_Max,
};
GWMMT_Msg: 转发消息到指定客户端
GWMMT_MsgAll: 转发广播消息到全部客户端
GWMMT_Droped: 网关管理器T掉指定客户端
GWMMT_DropedDiscard: 网关管理器T掉指定客户端
GWMMT_Shutdown: 关闭网关服务器
---------------------------------------------------------------------------------------
网关服务器 协议:
enum EGWSVMsgType
{
GWSMT_CLJoin, // New client join.
GWSMT_CLDisconnected, // Client disconnected.
GWSMT_Msg, // Route client message to server.
GWSMT_Ping, // gateway ping message to server.
GWSMT_Max,
};
GWSMT_CLJoin: 新连接客户端
GWSMT_CLDisconnected: 客户端连接断开
GWSMT_Msg: 将客户端的消息转发到服务器
GWSMT_Ping: 网关服务器到逻辑服务器的ping
-----------------------------------------------------------------------------------
网关服务器消息头:
struct GWMsgHdr
{
GWMsgHdr( u_int type, u_int len ):type_( type ), data_len_(len){}
u_int type_; // Msg type.
u_int data_len_; //
};
------------------------------------------------------------------------------------
网关上报给网关管理器的自身信息
struct GWLocalInfo  // 监听客户端连接的endpoint
{
u_int addr_; // Gateway listen address.
u_short port_;
};
------------------------------------------------------------------------------------
网关从网关管理器接受的信息
struct GWInfo
{
u_int id_; // Gateway server id.
u_int cl_num_; // Client num.
};

设计特点:
1.启动参数通过内存共享,本地,可以被其他程序访问到,也许直接被共享到集群了,没有深入去看
posted @ 2015-03-15 22:24 Enic 阅读(281) | 评论 (0)编辑 收藏

 64位windows平台,编译环境是VS2005,进入Visual Studio 2005 x64 Win64 Command Prompt(单纯的cmd也不一定不行,我没试)。把bjam.exe放在boost根目录下,进入根目录,执行:

    bjam --toolset=msvc address-model=64 --with-thread stage
    bjam --toolset=msvc address-model=64 --with-date_time stage
    关键选项:“address-model=64 ”
    64位Linux平台,使用gcc编译。进入boost根目录,执行:
    ./bjam --toolset=gcc --with-thread stage
    ./bjam --toolset=gcc --with-date_time stage
    linux平台下倒是简单,不过网上有篇文章介绍用如下命令编译,不知道是多此一举,还是适用于某些情况(非64位linux主机?)。
    ./bjam --toolset=gcc "-sBUILD=release <cxxflags>-m64" --with-thread stage
    ./bjam --toolset=gcc "-sBUILD=release <cxxflags>-m64" --with-date_time stage
 
    唉。命令都很简单,可浪费了我不少时间。usage根本没写,去看boost build的嘛,页数n多不说,看完之后能否找到答案还是未知数。网上相关资料很少而且大多南辕北辙,只好一直搜索+尝试。其实我只是想要个64位版本的库而已,这应该不是啥稀罕的需求吧?
   在windows平台下,编译出来的是否是64位类库,只有link 64位程序的时候才能发现。如果不是,link程序无法找到类库中定义的函数或者类。linux不知道,因为我整出来直接就是64位了,我也懒得再找一台32位linux主机折腾了。
posted @ 2015-03-08 17:57 Enic 阅读(363) | 评论 (0)编辑 收藏

并发编程几个基础库:
C++ REST SDK

C++ PPL
C++ TBB

OpenMP
OpenAMP
OpenCL
CUDA

posted @ 2015-02-10 19:03 Enic 阅读(130) | 评论 (0)编辑 收藏

前言

    在大家使用github的过程中,一定会碰到这样一种情况,就是每次要push 和pull时总是要输入github的账号和密码,这样不仅浪费了大量的时间且降低了工作效率。在此背景下,本文在网上找了两种方法来避免这种状况,这些成果也是先人提出来的,在此只是做个总结。

1.方法一 

1.1 创建文件存储GIT用户名和密码

在%HOME%目录中,一般为C:\users\Administrator,也可以是你自己创建的系统用户名目录,反正都在C:\users\中。文件名为.git-credentials,由于在Window中不允许直接创建以"."开头的文件,所以需要借助git bash进行,打开git bash客户端,进行%HOME%目录,然后用touch创建文件 .git-credentials, 用vim编辑此文件,输入内容格式:

touch .git-credentials

vim .git-credentials

https://{username}:{password}@github.com

1.2 添加Git Config 内容

进入git bash终端, 输入如下命令:

git config --global credential.helper store

执行完后查看%HOME%目录下的.gitconfig文件,会多了一项:

[credential]

    helper = store

重新开启git bash会发现git push时不用再输入用户名和密码

2.方法二

2.1 添加环境变量

在windows中添加一个HOME环境变量,变量名:HOME,变量值:%USERPROFILE%

2.2 创建git用户名和密码存储文件

进入%HOME%目录,新建一个名为"_netrc"的文件,文件中内容格式如下:

machine {git account name}.github.com
login your-usernmae
password your-password

重新打开git bash即可,无需再输入用户名和密码

posted @ 2015-01-31 12:59 Enic 阅读(154) | 评论 (0)编辑 收藏

个人的成功:从依赖到独立
习惯一:积极主动——个人愿景的原则
习惯二:以终为始——自我领导的原则
习惯三:要事第一——自我管理的原则
第三部分 公众的成功:从独立到互赖
习惯四:双赢思维——人际领导的原则
习惯五:知彼知己——同理心交流的原则
习惯六:统合综效——创造性合作的原则
习惯七:不断更新——平衡的自我更新的原则
再次由内而外造就自己
posted @ 2015-01-27 10:24 Enic 阅读(114) | 评论 (0)编辑 收藏

隐藏MFC对话框程序主窗口:
1.不在桌面任务栏显示
ModifyStyleEx(WS_EX_APPWINDOW, WS_EX_TOOLWINDOW);
2.使用SetPos设置0
::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, 0);

关闭主窗口对话框:
CDialog* pDlg =reinterpret_cast<CDialog*>(AfxGetApp()->GetMainWnd());
  pDlg->EndDialog(IDOK);

posted @ 2015-01-19 19:34 Enic 阅读(462) | 评论 (0)编辑 收藏

仅列出标题
共22页: First 3 4 5 6 7 8 9 10 11 Last