做游戏统计后台安卓开发的小游戏代码,都可以用什么代码或方式

游戏开发中统一支付系统设计与实现及python实例代码-python-Php教程-壹聚教程网游戏开发中统一支付系统设计与实现及python实例代码
我们游戏后台是 python 开发的,以前是不同的游戏款支付方式单独开发,感觉走支持流程比较痛苦,现在我们来来介绍游戏开发中统一支付系统的设计写实现。
笔者公司的游戏产品已经有几款了,每次上各种渠道都是要搭配不同的计费方式,并且每开发游戏都要重复一遍痛苦的接入sdk流程
游戏的支付需要出各种报表以及统计,每个游戏单独去做对人力的消耗巨大
基于以上几点,我这边设计了统一支付系统。
这个系列一共会分两篇文章,分别对应系统的v1版和v2版,我们这一篇先从v1起介绍。
在仔细分析了国内的大多数支付sdk之后,我们梳理出游戏的支付流程大体可以实现为两类:
第三方sdk服务器进行支付结果通知
第三方sdk客户端直接返回支付结果通知,没有服务器支付结果通知。
对于调用方而言,这两种方式各有好处。
第一种方式更加安全,但是支付调用的时间相对较长
第二种方式速度更快,但是很容易被不怀好意的人破解。
接下来,我们来看一下我这边设计的统一支付流程。
服务器端:
简单解释一下:
每次支付开始,都要让服务器生成一个订单作为此次支付的记录,订单的id即为 bill_id。订单有4中状态:订单生成,支付失败,支付成功,发货成功。
pay_server即为统一支付系统的服务器端,考虑到调用量和方便调试,使用了简单的http协议+json+sign的方式
对于服务器内部,唯一麻烦的一点是,《等待pay_server支付结果通知》这个接口。因为这个http请求需要支持挂起,在第三方支付服务器通知了pay_server之后,pay_server 根据通知里面透传的bill_id 将订单状态修改后,再给客户端结果。
由于我们后端是用python实现的,所以用django+gevent的实现还是蛮简单的,示例代码如下:
&&& if bill_id in bill_to_result:
&&&&&&& result = bill_to_result[bill_id]
&&&&&&& result = AsyncResult()
&&&&&&& bill_to_result[bill_id] = result
&&&&&&& ret, error = result.get(timeout=config.BILL_RESULT_TIMEOUT)
&&& except gevent.Timeout:
&&&&&&& logger.error('result wait timeout. bill_id: %s', bill_id)
&&&&&&& return jsonify(
&&&&&&&&&&& ret=config.RET_WAIT_BILL_RESULT_TIMEOUT,
&&&&&&&&&&& msg=u'超时没有返回'
&&& except:
&&&&&&& logger.fatal('exc occur. bill_id: %s', bill_id, exc_info=True)
&&&&&&& return jsonify(
&&&&&&&&&&& ret=config.RET_SERVER_BUSY,
&&&&&&&&&&& msg=u'服务器繁忙'
&&&&&&& logger.error('notify result succ. bill_id: %s', bill.id)
&&&&&&& return jsonify(
&&&&&&&&&&& ret=ret,
&&&&&&&&&&& error=error,
&&&&&&&&&&& sign=make_sign(secret, .path, bill.id)
&&& finally:
&&&&&&& if bill_to_result.get(bill_id) == result:
&&&&&&&&&&& bill_to_result.pop(bill_id, None)
当收到第三方支付服务器的结果通知时,添加如下代码即可:
def notify_bill_result(bill_id, ret, error):
&&& result = bill_to_result.get(bill_id)
&&& if result:
&&&&&&& result.set((ret, error))
再来看一下支付存储需要的字段是哪些:
class Bill(models.Model):
&&& userid = models.CharField(max_length=64)
&&& # 支付物品唯一ID
&&& item_id = models.CharField(max_length=255)
&&& source = models.CharField(max_length=32)
&&& # 渠道,类似 CN_91 这种
&&& channel = models.CharField(max_length=64, null=True, blank=True)
&&& create_time = models.DateTimeField(default=datetime.datetime.now)
&&& state = models.IntegerField(default=config.BILL_STATE_INIT, choices=[
&&&&&&& (config.BILL_STATE_INIT, u'订单生成'),
&&&&&&& (config.BILL_STATE_FAIL, u'支付失败'),
&&&&&&& (config.BILL_STATE_SUCC, u'支付成功'),
&&&&&&& (config.BILL_STATE_DELIVERED, u'发货成功'),
&&& bill_type = models.IntegerField(choices=config.BILL_TYPE_CHOICES)
&&& # 只有当是储值类型的会生效
&&& lz_amt = models.FloatField(null=True, blank=True)
&&& # 币种
&&& lz_cur = models.CharField(max_length=64, null=True, blank=True)
&&& # 兑换的内部游戏币
&&& lz_coin = models.IntegerField(null=True, blank=True)
&&& # 会被透传的信息
&&& passinfo = models.TextField(null=True, blank=True, default='')
&&& # 系统 android/ios
&&& os = models.CharField(max_length=16)
&&& # 应用的版本
&&& app_version = models.IntegerField(null=True, blank=True)
&&& # sdk的版本
&&& sdk_version = models.IntegerField(null=True, blank=True)
&&& # 如果是映射的用户,比如平安这种
&&& map_userid = models.CharField(max_length=255, null=True, blank=True)
&&& # 不可以用dict(),默认参数的问题一定要牢记
&&& extra = jsonfield.JSONField(default=dict, null=True)
需要注意的是extra字段,客户端可以将金额,描述等字段都放入其中。
整个支付的流程大体就是这样了,还需要多聊一点的是关于客户端支付sdk封装的问题。
目前是封装了一个统一支付sdk,将所有第三方sdk需要的参数传入,并根据传入的bill_type来启动对应的支付方式。
这样做的好处是:
只要调通一种支付方式,基本所有的支付都调试通过了
而缺点是:
依赖的第三方sdk太多,难以定制只使用几个sdk的版本
第三方sdk之间非常容易出现冲突
支持新的sdk非常麻烦,很容易影响其他支付
所以其实比较下来,这种客户端sdk的封装方式实际是比较差劲的,所以才会有了接下来的v2版本,升级的具体内容将会再下篇介绍。
一. 解决服务器端分布式的问题
解决这个问题的核心思路比较简单:
&&& 之前我们是把event的通知放在进程内存中,现在我们做成网络通信
由于支付的请求量本身部署高并发,所以就放弃了打算直接写通知server的想法,转而看一下有没有什么简单的解决方案。
而由于自己之前redis的使用经历,恰好知道redis有一个pubsub模式,很适合做这种监听和通知的工作。
python的实现示例代码如下:
import time
import config
from share.vals import rds
from share.utils import safe_str
from gevent.timeout import Timeout
from urllib import quote, unquote
class RedisPubSub(object):
&&& 用redis订阅/发布消息
&&& # 订阅频道
&&& sub_key = None
&&& pub_sub = None
&&& channel = None
&&& __prefix = 'unity_pay:%s'
&&& def __init__(self, channel):
&&&&&&& self.channel = channel
&&&&&&& self.sub_key = self.get_sub_key(self.channel)
&&& def __del__(self):
&&&&&&& self.unsubscribe()
&&& def subscribe(self):
&&&&&&& if self.pub_sub:
&&&&&&&&&&& return
&&&&&&& self.pub_sub = rds.pubsub()
&&&&&&& self.pub_sub.subscribe(self.sub_key)
&&& def unsubscribe(self):
&&&&&&& if not self.pub_sub:
&&&&&&&&&&& return
&&&&&&& self.pub_sub.unsubscribe()
&&&&&&& self.pub_sub = None
&&& def set(self, *args):
&&&&&&& &&&
&&&&&&& 设置订阅消息
&&&&&&& &&&
&&&&&&& rds.publish(self.sub_key, self.format(*args))
&&& def get(self, timeout=config.BILL_RESULT_TIMEOUT):
&&&&&&& &&&
&&&&&&& 获取订阅消息
&&&&&&& &&&
&&&&&&& self.subscribe()
&&&&&&& stamp = time.time() + timeout
&&&&&&& while time.time() & stamp:
&&&&&&&&&&& message = self.pub_sub.get_message(True)
&&&&&&&&&&& if message and message['type'] == 'message':
&&&&&&&&&&&&&&& return self.parse(message.get('data', ''))
&&&&&&&&&&& time.sleep(1)
&&&&&&& raise Timeout
&&& @classmethod
&&& def format(cls, *args):
&&&&&&& return ','.join([quote(safe_str(arg)) for arg in args])
&&& @classmethod
&&& def parse(cls, message):
&&&&&&& args = message.split(',')
&&&&&&& return [unquote(arg) for arg in args]
&&& @classmethod
&&& def get_sub_key(cls, channel):
&&&&&&& return cls.__prefix % channel
代码很简单,就不多解释了。
这样做的坏处是redis会有热点问题,不过反正redis中也不存放数据,找台热备机随时能切换即可。
&二. 客户端对支付sdk进行插件式管理十分困难
其实这个问题也不是很难,解决的关键是需要知道一个点:
&&& jar包在编译的时候需要所有的类都存在,但是当程序调用这个jar包时,这个jar包有些类不存在,并不会崩溃,而是报可被捕获的异常
基于这一点,我们就可以做一个同一个工厂函数,将这个工厂函数类封装成一个jar包。
同时,我们对每一种支付方式,都封装出一个统一的接口,而工厂函数返回的即这样一个接口的实现。当某一种支付方式的封装类不存在时,就捕获这个异常,并返回NULL。
统一接口的代码如下:
public abstract class PaymentInterf {
&&&& * 初始化统一方法
&&&& * @param context 上下文
&&&& * @param parameters 初始化时需要的参数 数组
&&&& * 1、移动 parameters--》mmid 、mmkey
&&&& * 2、联通&&&& parameters:
&&&& * string appid 应用编号
&&&& * string cpCode 开发商编号
&&&& * string cpid 开发商VAC资质编号
&&&& * string company 开发者公司名字
&&&& * string phone 开发者客服电话号码
&&&& * string game 应用名称
&&&& * UnipayPayResultListener mCallBack 初始化函数回调结果(目前 只有联通多了一个非String类型的参数)
&&&& * 3、Amazon初始化
&&&& * suk
&&&& * callBack
&&& public abstract void init(Context context,Object...parameters);
&&&& * 支付统一函数
&&&& * @param context 上下文
&&&& * @param parameters 支付时需要传递的参数& 如 payCode billId 。。。
&&& public abstract void pay(Context context,Object...parameters);
&&& public static void billDeliver(String appKey, String billID) {
&&&&&&& HttpLobbiesService.g().billDeliver(appKey, billID, new HttpLobbiesService.Callback() {
&&&&&&&&&&& @Override
&&&&&&&&&&& public void onResult(ResultInfo info) {
&&&&&&&&&&&&&&& if (info != null && info.getRetCode() == 0) {
&&&&&&&&&&&&&&&&&&& Log.e(&billDeliver&, info.getRetCode() + &/:& + info.getObj());
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
&&&&&&& });
工厂函数的代码如下:
public class PaymentFactoy {
&&& public static PaymentInterf producePay(int billTypy){
&&&&&&& try {
&&&&&&&&&&& switch (billTypy) {
&&&&&&&&&&& case Constant.BILL_TYPE_MMCNCCPAY:
&&&&&&&&&&&&&&& return MMPayInstance.getInstance();
&&&&&&&&&&& case Constant.BILL_TYPE_DIANXIN:
&&&&&&&&&&&&&&& return CTEStoreInstance.getInstance();
&&&&&&&&&&& case Constant.BILL_TYPE_UNIPAY:
&&&&&&&&&&&&&&& return UnicomInstance.getInstance();
&&&&&&&&&&& case Constant.BILL_TYPE_TAOBAO:
&&&&&&&&&&&&&&& return TaoBaoInstance.getInstance();
&&&&&&&&&&& case Constant.BILL_TYPE_WEIPAY:
&&&&&&&&&&&&&&& return WeiPayInstance.getInstance();
&&&&&&&&&&& case Constant.BILL_TYPE_WIMI:
&&&&&&&&&&&&&&& return WeiMiInstance.getInstance();
&&&&&&&&&&& default:
&&&&&&&&&&&&&&&
&&&&&&&&&&& }
(Exception e) {
&&&&&&&&&&& e.printStackTrace();
&&&&&&&&&&&
上面的方法是只封装了一个factory函数的jar包,其他的对每种支付的封装还是走源码的方式。
其实最早的时候,我是想将每种支付的封装也做成jar的方式,后来公司同事做成了现在的这种方式,我考虑了一下,可能确实用源码的方式更好,原因如下:
&&& 源码的方式,方便调试
&&& 源码的方式,当编译进游戏的时候,如果某种支付忘记引入对应的jar包,会直接报错提醒
&&& 每个支付方式一个jar包的话,维护成本过高
总体差不多就是这样。
上一页: &&&&&下一页:相关内容【H5小游戏开发教程】微信游戏中如何添加数据统计代码?
在游戏中加入统计代码是很必要的,通过数据统计你才能知道你分享出去的游戏到底传播的效果是怎么样的,服务器是否能承受的住,是否应该换换营销的策略了。
下面讲一下如何添加数据统计代码吧!
以CNZZ的数据统计为例:第一步:注册cnzz账号,网站地址: 第二步:添加站点
第三步:添加站点的信息
第四步:复制统计代码的js代码
最后一步:添加到你的游戏页面的底部
注意:在这一步你肯定想把这个统计的图标或者文字作个隐藏的,那么就用css样式来设置为隐藏即可,代码如下:&div style='display:none'&& & & //这里添加你的统计代码的js& & &&/div&
1#&& 08:58:26&
请问能不能统计游戏的转发量呢,像微信的后台那样?游戏后台服务端开发,架构设计,流程
本人想从事游戏后台开发方面的工作,一直从事linux c/c++后台的业务软件开发,想在这里认识从事过游戏后台开发方面的朋友,咨询了解这方面工作的一些问题(收费的也可以),哪位热心的朋友,如果可以的话,请留言,然后qq联系,非常感谢!
一般c++加lua
--- 共有 2 条评论 ---
感谢您的回答,谢谢!
云风的skynet还不错的。
可以加我,聊聊。。
--- 共有 1 条评论 ---
感谢您的关注!qq已经发私信给您了!
/cloud/DevNotes
--- 共有 1 条评论 ---
谢谢,谢谢!我看看
网络部分:
游戏后台跟普通后台业务软件相同,都是追求高性能高并发,相关书籍可以看《UNIX网络编程》(UNP)《C++网络编程》(ACE),相关框架/库可以使用libev,libuv,libevent,grpc等。
逻辑部分:
LEVEL1:弱交互,无状态,结果立即返回,后台实现也就是个查数据库,计算,返回的过程。这个在国内的游戏活动上用的最多。常规MVC模式即可应付。这种最简单。
LEVEL2:页游RPG,弱交互,弱状态,诸如会有移动/回合战斗等信息。如果信息准确性并不重要,那不需要模拟运动状态实时更新信息,也可以简单设置起终点,快速运算整场战斗(战斗开始即知道结果),由客户端自行模拟运动播放动画。这种也不麻烦。
LEVEL3:页游ARPG,强交互,弱状态,因为攻击是实时操作,与位置有关,早先贪图方便,服务端沿用LEVEL2的方式,由客户端自行计算判断,导致外挂横行(飞城,秒人),以及在精细交互下暴露的同步问题(隔几个屏幕也被人KO,延时而非外挂)。一般应对方式为服务端模拟过程,在客户端发起操作时进行校验,这些东西翻翻游戏开发的书(国内)/网上一些博客比较常见,或者找代码看看。
LEVEL4:ARPG/AI,怪物的自动移动攻击啥的,开始涉及寻路的问题,常见的A*搜索算法,模拟运动。这块的处理比较暧昧,按理说这种全局性的东西服务器处理更为准确(毕竟要广播),这样也不怕作弊。但像DNF则是有一个”绑定“的做法,把AI放到了客户端做,所以才有了早先那么多秒图挂的存在,甚至自动过图机器人。但这么做有时候也是出于无奈,AI算法很多都是模拟运动,计算量都不小,无论是单线程跑一整套游戏逻辑和状态控制,还是多线程分散,都是具大的负担。比起只为一个人服务的单机游戏(采取远离玩家XXX米就disactive的做法),服务端所有单位都是active的。不是一个量级。
LEVEL5:实时交互,精准状态。格斗游戏,ACT游戏中常见,大型ARPG(DOTA,LOL)。操作要求低延时(100ms以上就没法玩了,在LEVEL4以上有时候为了弥补延时而进行的补偿,预判定等取巧方式不再奏效),蹲姿,站姿,跳跃,格挡,技能,搓招,上中下三路。一般说来,最容易实现的方式是在服务器上做完整模拟,严格按照逻辑时间刷新全局状态,状态转换时向客户端发送广播和通知,客户端同样自行模拟一套对等结构(动画,声音,UI啥的服务器管不了不是?),这种方案也常在LEVEL4中用。优点,理论上逻辑脚本前后端通用,原本做过单机的人也很容易理解并从事后端开发,方案符合人类思维,很容易想到。缺点,对网络稳定性依赖非常严重,任何一个状态更新包都不能丢,否则就会产生前后端不同步,虽然只是表现与逻辑的不同步,但服务器逻辑在设定你在往东走时,客户端表现你却还在原本的往西走中,然后咋滴你懂的。另外就是并发的问题,这种服务器并发一般不会太高。
--- 共有 1 条评论 ---
非常感谢这位朋友的回答,衷心表示感谢,我阅读消化一下,后续考虑是否选为最佳答案[讨论] 跪求一款软件,不知道叫什么,后台波浪形统计游戏帧数的软件
后台可以波浪形统计游戏的的帧数,不是MSI Afterburner和RivaTuner Statistics Server这两款软件
你说的是游戏自带的那个?
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=]逍遥派弟子[/uid] ( 11:41)[/b]好像不是,我没有找到那个后台统计的选项
shift+ctrl+N ?
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=387468]raymondwang[/uid] ( 11:46)[/b]游戏再带的?在哪里看
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=]ossama[/uid] ( 11:49)[/b]应该不是吧
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=]一介刀客[/uid] ( 11:54)[/b]什么意思。。。
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=1362[/uid] ( 11:48)[/b]加加好像有,刚下的,还没试
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=1362[/uid] ( 12:50)[/b]游戏中的网络性能图开关快捷键啊
游戏里就自带了 shift+ctrl+N [img]./mon_/crQ2f-336oXdZ3kT3cS11t-k0.png[/img]
游戏加加,没玩游戏之前是看不到统计数据的,你进去打一把出来就知道了
[b]Reply to [pid=88148,1]Reply[/pid] Post by [uid=]J丶King[/uid] ( 14:01)[/b]哦,我试一下看看,可以统计WOW吗}

我要回帖

更多关于 游戏开发代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信