Qt5.8《网络版够级游戏》编写日志之三:参数配置及数据库访问功能实现
无论是服务器端还是客户端都是有一些固定参数进行配置的,同时由于需要用到数据库,因此还需要在服务器端完成服务器端数据库访问方面的操作,作为公共基础的东西,现在就把它们都实现吧。
1 服务器端参数配置类及数据库访问类实现
1.1 数据库访问类实现
1.1.1 数据库访问类主要方法说明
数据库访问主要是讲访问数据的一些方法集中起来整合成一个类,供服务器访问数据时使用,这里采用的是SqlLite数据库。在GoGeServer项目中添加C++类,选择该类继承自QObject。整个服务器需要进行数据库存储的数据,目前只想到一个就是用户信息表的建立,用户信息表(姓名<Name>、密码<Password>、头像索引<headIcon>、积分<score>、在线状态<status>),其中以Name作为主键。围绕用户信息表,在SDataBase中实现了以下几个功能:
1) voidresetDataBase();数据库重置,即完成数据库所有表以及数据的删除,同时再对数据库进行建表以及初始数据的插入工作,并建立与数据库的连接;
2) voidloadDataBase();数据库加载,准确说是数据库连接函数,即建立一个够级用的数据库连接。
3) voidcreateDataBase(bool flag);创建数据库,根据flag的状态确定是调用resetDataBase()还是loadDataBase()来创建数据库连接。
4) intloginServer(QString Name,QString Password);登陆服务器查询,依据给定的Name和Password查询该用户是否存在,并返回响应的值,表示查询情况。
5) intregServer(QString Name,QString Password,qint32 HeadIcon);注册用户,根据提交的Name、Password、HeadIcon,来进行用户信息注册。
6) voidoffLine();用户下线信息,这个函数用于实现所有用户在线状态置离线的操作,主要是在手动关闭服务器时使用。
7) voidoffLine(QString Name);以Name为关键字,实现置Name用户为下线状态。
8) 最后还定义了一个信号,用于将数据接口访问类方法使用时的一些信息传送给主界面显示。
1.1.2 数据库访问类方法实现代码
1.1.2.1 resetDataBase()
void SDataBase::resetDataBase()
{
//判断DataBase是否打开,打开的话,则首先关闭
if(OpenFlag)
Database.close();
//添加“QSQLITE”,如果是其他数据库类型,则改变
Database =QSqlDatabase::addDatabase("QSQLITE");
//设置数据库名称、用户名、密码,因为这是SQLITE就没有设用户名和密码所以这里就置设定了数据库名称,如果是其他数据库还是进行用户名、密码的设置
Database.setDatabaseName("GoGeServer.db");
//打开数据
if(!Database.open())
{
//打开不成功的话,置OpenFlag为false,同时向主界面发送数据库连接不成功信号
OpenFlag = false;
emit sendMsg(0,"数据库连接不成功");
return;
}
//连接成功后,因为是重置数据库,所有先将数据中Player表删除,然后再重新创建Player表,然后再插入三条默认的用户记录,最后想主界面发送数据库重新创建和连接成功信号。
OpenFlag = true;
QSqlQuery Query;
Query.exec(QObject::tr("drop table Player"));
Query.exec(QObject::tr("create table Player (name vchar primarykey,password vchar,headIcon int,score int,status vchar)"));
Query.exec(QObject::tr("insert into Player values('Dave','123',1,0,'OffLine')"));
Query.exec(QObject::tr("insert into Player values('Lucy','123',11,0,'OffLine')"));
Query.exec(QObject::tr("insert into Player values ('Nancy','123',15,0,'OffLine')"));
emit sendMsg(0,"重新创建数据库并连接成功");
}
1.1.2.2 loadDataBase()
功能比较简单就是连接已有的数据库而已,连接成功就向主界面发送成功的信号信息,否则发送不成功的信号信息。
void SDataBase::loadDataBase()
{
if(OpenFlag)
Database.close();
Database = QSqlDatabase::addDatabase("QSQLITE");
Database.setDatabaseName("GoGeServer.db");
if(!Database.open())
{
OpenFlag = false;
emit sendMsg(0,"数据库连接不成功!");
}
else
{
OpenFlag = true;
emit sendMsg(0,"数据库连接成功!");
}
}
1.1.2.3 createDataBase(bool flag)
创建数据库的方法,其根据flag的状态,选择是直接连接数据库,还是重置数据库操作并连接数据库。
void SDataBase::createDataBase(bool flag)
{
if(flag)
loadDataBase();
else
resetDataBase();
}
1.1.2.4 loginService(QString Name, QStringPassword)
用户登陆服务器,根据用户输入的姓名和密码,来返回登陆信息,返回 0:失败,无用户或者密码错误,返回 1**:失败,该用户已上线,返回 2**:成功登陆,**表示该用户的头像索引,这里取了个巧,把用户的头像索引包含在了返回值中。在该方法中,对需要对已经登陆进行判断。
int SDataBase::loginServer(QString Name,QString Password)
{
if(!OpenFlag)
return 0;
QSqlQuerysql_query(Database);
//设置查询条件,这里查询用户登陆状态和头像索引
QString select_sql = "select status,headIcon from Player where name= :name and password = :password";
sql_query.prepare(select_sql);
//将用户的姓名、密码输入值传入
sql_query.bindValue(":name",Name);
sql_query.bindValue(":password",Password);
//查询
if(!sql_query.exec())
{
//如果查询失败,显示失败原因
qDebug()<<__FUNCTION__<<sql_query.lastError();
return 0;
}
else
{
//查询成功,也就是用户名和密码对了
if(sql_query.first())
{
QString loginStatus = sql_query.value(0).toString();
qint32 HeadIcon = sql_query.value(1).toInt();
//判断用户是不是不在线
if(loginStatus == "OffLine")
{
//向服务器主界面发送登陆成功信息,该信息将显示在主界面的服务器信息QtextEdit
emit sendMsg(0,Name+"登陆成功!");
//同时将该用户的登陆状态置为已登陆状态
QString update_sql ="update Player set status = 'Online' where name = :name";
sql_query.prepare(update_sql);
sql_query.bindValue(":name",Name);
sql_query.exec();
return 200+HeadIcon;
}
else
{
//如果用户的状态是在线状态,则想服务器发送重复登陆的信息
emit sendMsg(0,Name+":重复登陆被拒绝");
return 100+HeadIcon;
}
}
else
{
//如果查询,没有查到该用户或者密码不对,则向服务器主界面发送不正确信息
emit sendMsg(0,Name+":用户名或密码不正确!");
return 0;
}
}
return 0;
}
1.1.2.5 regServer(QString Name, QStringPassword, qint32 HeadIcon)
用户注册方法,根据用户提供的用户名、密码、头像索引信息进行用户注册操作,注册时要注意该用户名是否已被注册。
int SDataBase::regServer(QString Name,QString Password, qint32 HeadIcon)
{
if(!OpenFlag)
return 0;
//首先查找是否已经有同样的用户名存在
QSqlQuery sql_query(Database);
QString select_sql = "select * from Player where name =:name";
sql_query.prepare(select_sql);
sql_query.bindValue(":name",Name);
if(!sql_query.exec())
{
qDebug()<<__FUNCTION__<<sql_query.lastError();
return 0;
}
else
{
if(sql_query.isValid())
{
//如果找到有同名的用户名存在,则返回有重名信息
return 1;
}
else
{
//没有找到重名的用户名,则开始新用户信息插入工作
sql_query.clear();
QString update_sql = "insert into Playervalues(:name,:password,:headIcon,:score,:status)";
sql_query.prepare(update_sql);
sql_query.bindValue(":name",Name);
sql_query.bindValue(":password",Password);
sql_query.bindValue(":headIcon",HeadIcon);
sql_query.bindValue(":score",0);
sql_query.bindValue(":status","OffLine");
sql_query.exec();
//向服务器主界面发送新增用户信息
emit sendMsg(0,"新增用户:"+Name);
return 2;
}
}
return 0;
}
1.1.2.6 offLine ()
将所有用户置为下线状态,这个没什么好说的。
void SDataBase::offLine()
{
QSqlQuery sql_query(Database);
QString update_sql = "update Player set status = :status";
sql_query.prepare(update_sql);
sql_query.bindValue(":status","OffLine");
sql_query.exec();
}
1.1.2.7 offLine (QString Name)
将Name指定的用户置为下线状态,这个也没什么好说的。
void SDataBase::offLine(QString Name)
{
QSqlQuery sql_query(Database);
QString update_sql = "update Player set status = 'OffLine' wherename = :name";
sql_query.prepare(update_sql);
sql_query.bindValue(":name",Name);
sql_query.exec();
}
1.2 服务器端配置文件类实现
该类主要用户记录存放一些服务器运行所需的基本信息,使用QSetting类实现ini配置文件的创建和读取,这些基本信息因为用的地方较多,因此将实例化一个全局的对象,供服务器端的各个类使用。这里也将SDataBase类作为一个成员放在该类中,因为SDataBase也需要在多出使用。服务器端配置文件类我取名叫SconfigSet。
1.2.1 主要成员
由于想到的统一使用的一些配置信息还不多,因此实现的功能比较有限,主要有这么几个成员,这些都通过ini配置文件进行统一配置。
quint32 MaxPlayer;//服务器最大可登陆用户数
quint32 MaxRoom;//服务器最大可创建房间数
quint32 ServerPort;//服务器TCP端口
SDataBase ServerDataBase;//数据库访问类
QString ServerIP;//服务器IP地址
1.2.2 主要方法
方法不太多,就是三个,一个配置文件创建及初始化方法,一个是配置文件读取,一个是统一加载的方法。
1.2.2.1 loadConfig()
配置文件加载方法,在该方法内,判断是否存在配置文件,如果不存在配置文件,则调用resetConfig()方法进行配置文件的创建,并用默认值进行初始化;如果存在,则将配置文件中的信息读取到响应的成员变量中。
void SConfigSet::loadConfig()
{
qint32 CreateOrNot = 0;
QSettings *settings;
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
//查看是不是第一次运行程序,通过CreateOrNot来判断
settings->beginGroup("General");
CreateOrNot = settings->value("CreateOrNot").toInt();
settings->endGroup();
delete settings;
settings = NULL;
//判断ini文件是否存在,是通过读取配置文件中CreateOrNot键值,读到且等于5201314,则认为服务器文件存在,如果不是则认为文件不存在需要重新创建
if(CreateOrNot == 5201314)
{
//不是首次运行程序,直接对数据库进行加载
ServerDataBase.createDataBase(true);
//读取配置文件
readConfig();
emit sendMsg(0,"配置文件读取成功!");
}
else
{
//首次运行程序,对数据库进行初始化
ServerDataBase.createDataBase(false);
//创建配置文件,并初始化配置参数
resetConfig();
emit sendMsg(0,"首次运行服务器,创建配置文件成功!");
}
}
1.2.2.2 resetConfig ()
重新创建配置文件,并用默认值对成员变量进行初始化工作。
void SConfigSet::resetConfig()
{
QSettings*settings;
//打开配置文件,配置文件默认为放在程序运行目录下
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
//读取响应的配置文件信息,首先进入“General”节点,然后读取该节点下的数据,目前仅创建了这个节点。配置文件生成后的情况如下:
// [%General]
// CreateOrNot=5201314
// MaxPlayer=24
// MaxRoom=4
// ServerIP=127.0.0.1
// ServerPort=29300
settings->beginGroup("General");
MaxPlayer = 24;
MaxRoom = 4;
ServerPort = 29300;
ServerIP = "127.0.0.1";
//设置响应键值的数据
settings->setValue("CreateOrNot",5201314);
settings->setValue("MaxPlayer",MaxPlayer);
settings->setValue("MaxRoom",MaxRoom);
settings->setValue("ServerPort",ServerPort);
settings->setValue("ServerIP",ServerIP);
settings->endGroup();
delete settings;
settings = NULL;
}
1.2.2.3 readConfig ()
读取ini配置文件配置信息。
void SConfigSet::readConfig()
{
QSettings *settings;
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
settings->beginGroup("General");
ServerIP = settings->value("ServerIP").toString();
MaxPlayer = settings->value("MaxPlayer").toUInt();
MaxRoom = settings->value("MaxRoom").toUInt();
ServerPort = settings->value("ServerPort").toUInt();
settings->endGroup();
delete settings;
settings = NULL;
}
至此,服务器的参数配置类和数据库访问类编码已基本实现。个人觉得虽然我把一些常用的东西进行了统一,但是还是有很多优化的地方,后续边写编完善吧。
2 客户端参数配置类功能实现
客户端参数配置类的实现与服务器端的实现基本一致,只是配置参数不一致而已,下面将成员和主要方法直接列出代码。客户端参数配置类我取名叫SConfigSet。
2.1 主要成员
quint32 ServerPort;//服务器端口
QString ServerIP;//服务器IP
QString LastPlayer;//最近登陆者登陆名
2.2 主要方法
2.2.1 loadConfig()
void SConfigSet::loadConfig()
{
qint32 CreateOrNot = 0;
QSettings *settings;
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
//查看是不是第一次运行程序,通过CreateOrNot来判断
settings->beginGroup("General");
CreateOrNot = settings->value("CreateOrNot").toInt();
settings->endGroup();
delete settings;
settings = NULL;
if(CreateOrNot == 5201314)
{
//读取配置文件
readConfig();
}
else
{
//创建配置文件,并初始化配置参数
resetConfig();
}
}
2.2.2 resetConfig()
void SConfigSet::resetConfig()
{
QSettings *settings;
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
settings->beginGroup("General");
ServerPort = 29300;
ServerIP = "127.0.0.1";
LastPlayer = "Dave";
settings->setValue("CreateOrNot",5201314);
settings->setValue("ServerPort",ServerPort);
settings->setValue("ServerIP",ServerIP);
settings->setValue("LastPlayer",LastPlayer);
settings->endGroup();
delete settings;
settings = NULL;
}
2.2.3 readConfig()
void SConfigSet::readConfig()
{
QSettings *settings;
settings = newQSettings(QDir::currentPath()+"/serverConfig.ini",QSettings::IniFormat);
settings->beginGroup("General");
ServerIP = settings->value("ServerIP").toString();
LastPlayer = settings->value("LastPlayer").toString();
ServerPort = settings->value("ServerPort").toUInt();
settings->endGroup();
delete settings;
settings = NULL;
}
3 小结
至此,服务器和客户端的参数配置类以及数据库访问类的功能基本实现,为了实现这些基本信息的全局调用,我在客户端和服务器端里又新建了一个Global类,这个类中放了一些全局使用的变量以及一些结构的定义,这个随着编写的深入,Global中的内容相对来说会慢慢丰富。目前,在Global类中就是定义了SconfigSet的实例,供全局使用。