weijiº a15971596685
这个人很懒,什么都没有留下
关注数: 13 粉丝数: 33 发帖数: 1,124 关注贴吧数: 5
CS连接数据库遇到的坑,与解决方法 注:这只是针对AMXX1.8.1 1.8.1以上的版本我没接触过 怎么说呢,很久没去写Pawn了,后来帮朋友写东西遇到了很多坑,其中数据库的就比较多,我就把我碰到的坑分享下,虽然CS玩的人越来越少了,但是分享下也不是什么坏事单击也是可以用的 关于数据保存方式AMXX很贴心给了三种方式 nvault_amxx.dll sqlite_amxx.dll mysql_amxx.dll nvault 大家可能比我用的还熟练就不多说了 sqlite 这个是内置的一个数据库,可以理解为嵌入式数据库,但是他的库保存在本地,如果你需要远程连接这个根本不靠谱 mysql 这个就是主角了 连接数据库的头文件 #include <dbi>//这个方法我使用了很多次但是他只连接sqlite成功了,但是这不是我想要的预期,所以直接放弃了,如果有会的可以补充下) #include <sqlx> 先跳过一些变量和头文件 如下: #include <amxmodx> #include <amxmisc> #include <sqlx> #define PLUGIN"Mysql" #define VERSION"1.0" #define AUTHOR"weiji" new Handle:g_SqlTuple new Handle:SqlConnection new g_Error[511] new g_host[33]; new g_user[33]; new g_pass[33]; new g_dbname[33]; new g_tabname[33]; new g_MsgSync 一、链接Mysql的坑 SQL_MakeDbTuple(const host[], const user[], const pass[], const db[], timeout=0); 根据官网的介绍他只是缓存连接信息,但是他一致报错,而且官网的介绍与我写的基本相同,timeout官网展示默认的是0也就是无限制,是选填的 SQL_MakeDbTuple("127.0.0.1","root","password","CS"); 但是他报错了,第一时间分析原因为何链接失败,发现没写端口,官网文档可能比较老,没有说明需要填写端口(Mysql.dll可能制作人没有考虑到或者确实太老了没有把端口独立出来) 修改SQL_MakeDbTuple("127.0.0.1:3306","root","password","CS"); 再次运行,又报错了这次奇怪的是SQL_PrepareQuery报错,这次就很纳闷了 端口也写了 ,再次翻查官网的信息 SQL_SetAffinity(const driver[]); 他需要我指定驱动,嗯?我激活的Mysql驱动还需要让他也知道?写上,SQL_SetAffinity("mysql") 再次运行 好像没问题了 再试一次 出现mysql错误 1050 - Table 'test' already exists 重复建表,现在修改表验证 把 CREATE TABLE test 修改成 CREATE TABLE IF NOT EXISTS 然后就可以取消这个地方的错误提示了 意思是如果存在就不创建 如果不在就创建 然后视乎一切都没什么问题了服务器也显示了 [-------Mysql---------] MySQL connect OK 现在就要开始保存数据和读取数据了 以下代码我就不多做解释了 不懂的可以百度 public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR); g_MsgSync = CreateHudSyncObj() register_cvar("amx_sql_host", "IP"); register_cvar("amx_sql_user", "账号"); register_cvar("amx_sql_pass", "密码"); register_cvar("amx_sql_db", "数据库"); register_cvar("amx_sql_table", "表"); new configsDir[64]; get_configsdir(configsDir, 63); server_cmd("exec %s/sql.cfg", configsDir);//读取sql.cfg 如果不想自带的用可以修改文件名自己创建一个 server_exec(); } public plugin_cfg() { get_cvar_string("amx_sql_host",g_host,32); get_cvar_string("amx_sql_user",g_user,32); get_cvar_string("amx_sql_pass",g_pass,32); get_cvar_string("amx_sql_db",g_dbname,32); get_cvar_string("amx_sql_table",g_tabname,32); MySql_Init(); } public MySql_Init() { SQL_SetAffinity("mysql") g_SqlTuple = SQL_MakeDbTuple(g_host,g_user,g_pass,g_dbname); static error[128] new errcode if ( g_SqlTuple ) SqlConnection = SQL_Connect(g_SqlTuple, errcode, error, charsmax(error)) if ( SqlConnection == Empty_Handle ) { server_print("MySQL connect error: [%d] '%s' (%s,%s,%s)", errcode, error, g_host, g_user, g_dbname)//显示报错信息 SQL_FreeHandle(g_SqlTuple) g_SqlTuple = Empty_Handle return } new Handle:Queries; Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS %s (`UID` int(11) NOT NULL AUTO_INCREMENT,`NAME` varchar(20) DEFAULT NULL DEFAULT NULL,PRIMARY KEY (`UID`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET utf8;", g_tabname); if(!SQL_Execute(Queries)) { SQL_QueryError(Queries,g_Error,charsmax(g_Error)); set_fail_state(g_Error); } SQL_FreeHandle(Queries); SQL_FreeHandle(SqlConnection); server_print("[-------Mysql---------] MySQL connect OK") } public plugin_end() SQL_FreeHandle(g_SqlTuple); 二、读取和保存Mysql的坑(这里主要是MYSQL) 链接成功我们测试下能否读取数据库,在数据添加自己的名字信息 先说读取,我这边写的进入服务器读取,退出服务器保存,我们记录用户如果没有使用其他插件辅助,或者你不是正版CS就没有唯一识别的方法,那么怎么办,保存名字好像可以,禁止他们服务器改名字就行了 我们先获取进入服务器的玩家名字,然后去数据库查询看能否成功,如果他名字没有中文恭喜你,你可以放心了,但是现在很多服务器都是会有中文的,于是我退出服务器查看数据库多了一段乱码,是退出的时候保存的因为我写的方法是,如果找不到就创建字段具体看下面代码,好家伙,直接给我整懵逼了,不过这些乱码好像是因为数据库默认格式是latin1那么保存的可能也是latin1,测试下,把原来的sql语句做下处理 (这里用IGNORE与创建表原理一样不能让他重复创建) INSERT IGNORE INTO test (NAME) VALUES ('%s'); 改成 这话的意思是 把表test中,字段NAME编码为latin1写入后的结果转换为utf8编码结果 INSERT IGNORE INTO test (NAME) VALUES (convert(unhex(hex(convert('%s' using latin1))) using utf8)); 这下保存解决了乱码问题,再试一次,依旧失败,看来CS读取也是latin1 SELECT UID FROM test WHERE NAME = '%s' ; 改成 这话的意思是 把表test中,字段NAME编码为utf8查询后的结果转换为latin1编码结果 SELECT UID FROM test WHERE convert(unhex(hex(convert(NAME using utf8))) using latin1) = '%s' ; 读取成功 public client_disconnect(id) { Save_MySql(id) } public client_putinserver(id) { set_task(1.0, "Load_MySql", id) } //读取数据 public Load_MySql(id) { new szTemp[ 512 ] , Data[ 1 ] new name[32] get_user_name(id,name,31) Data[ 0 ] = id; formatex( szTemp , charsmax( szTemp ) ,"SELECT UID FROM test WHERE convert(unhex(hex(convert(NEWNAME using utf8))) using latin1) = '%s' ;" , name) SQL_ThreadQuery( g_SqlTuple , "register_client" , szTemp , Data , sizeof( Data ) ) } public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize) { if(FailState == TQUERY_CONNECT_FAILED) { log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error) } else if(FailState == TQUERY_QUERY_FAILED) { log_amx("Load Query failed. [%d] %s", Errcode, Error) } new id = Data[ 0 ]; new name[32] get_user_name(id,name,31) //如果查询失败就创建 if ( !SQL_NumResults( Query ) ) { new szTemp[ 256 ] //创建信息 formatex( szTemp , charsmax( szTemp ) ,"INSERT IGNORE INTO test (NAME) VALUES (convert(unhex(hex(convert('%s' using latin1))) using utf8));",name) SQL_ThreadQuery( g_SqlTuple , "IgnoreHandle" , szTemp ) } else { g_uid[ id ] = SQL_ReadResult( Query , 0 ) return PLUGIN_HANDLED } //保存数据 public Save_MySql(id) { new szTemp[512] //这里我是用的UID因为创建后会自动生成UID,如果没有辅助插件,这样会出现首次数据不保存的情况, format(szTemp,charsmax(szTemp),"UPDATE test SET xp = '%d' , lv = '%d' WHERE UID = '%d';", g_xp[id],g_lv[id],g_dzuid[id]) SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp) } public IgnoreHandle(FailState,Handle:Query,Error[],Errcode,Data[],DataSize) { SQL_FreeHandle(Query) return PLUGIN_HANDLED } 三、附加数据库道具卡或权限卡方式 SELECT UID,ITEM FROM test WHERE convert(unhex(hex(convert(NEWNAME using utf8))) using latin1) = '%s' SQL_ReadResult( Query , 0 ) 这个0代表查询的字段顺序 mysql默认从0开始,那么我这边选择的UID就是0 ITEM就是1,另外这个数据读取方式只能用于整数和浮点和布尔 SQL_ReadResult(Query, 1, item1[id], charsmax(item1[])) 这是读取字符串的方式,获取字段1ITEM的内容然后你们懂得
1 下一页