凡人修真3D(3)神翼

1.对于一些集成起来的通用配置表,如下面这样的:


还是上图吧

这种通用表,字段直接是value1,value2,value3,value4,value5,value6。

这是GameConfigData里面的类:

	class CGodWingJinJieBase
		:public cdf::CRefShared
	{
	public:
		Message::Db::Tables::TGodWingConfig _godWingJinJieConfig;
	};
	typedef cdf::CHandle<CGodWingJinJieBase> GodWingJinJieBasePtr;
这是使用的代码:
		if(!CGodWingConfigManager::instance()->getJinJieInfoConfig(playerGodWing.jinJieLevel, godWingJinJieConfig))
		{
			CErrorCodeManager::throwException("Public_GameConfigDataError");
		}

		//判断是否已拥有该皮肤
		if(!godWingManager->isSkinExist(godWingJinJieConfig.value6, true))
		{
			//添加皮肤
			godWingManager->addGodWingSkin(godWingJinJieConfig.value6, "", gateEntity, player);

			//设置皮肤
			godWingManager->setGodWingSkin(godWingJinJieConfig.value6, player, gateEntity);
		}

这样直接使用value6,以后每个人看到这里的代码,都要去数据库查一下,还得对照一下其他代码。非常麻烦。

在GameConfigData读取的时候应该进行封装,转换成可读取的字段,而不是直接使用。

修改后:

class CGodWingJinJieConfig
		:public cdf::CRefShared
	{
	public:
		int jie;//阶
		int jiePropNum;//消耗进阶道具数量
		int blessValue;//祝福值
		int attributeId;//属性
		int skillCode;//技能
		int skinCode;//皮肤 
	};
	typedef cdf::CHandle<CGodWingJinJieConfig> CGodWingJinJieConfigPtr; 
	typedef std::map<int, CGodWingJinJieConfigPtr> MapCGodWingJinJieConfig;
Message::Db::Tables::SeqTGodWingConfig::const_iterator iter1 = tGodWingConfigs.begin();
	for(; iter1 != tGodWingConfigs.end(); iter1++)
	{
		switch(iter1->type)
		{
		case 1://神翼升星的经验
			{
				_starExpMap[iter1->value1][iter1->value2] = iter1->value3;
			}
			break;

		case 2://阶的数据
			{
				CGodWingJinJieConfigPtr jinJenConfig = new CGodWingJinJieConfig();
				jinJenConfig->jie = iter1->value1;
				jinJenConfig->jiePropNum = iter1->value2;
				jinJenConfig->blessValue = iter1->value3;
				jinJenConfig->attributeId = iter1->value4;
				jinJenConfig->skillCode = iter1->value5;
				jinJenConfig->skinCode = iter1->value6;
				_jinJieMap[iter1->value1] = jinJenConfig;
			}
			break;
这样取出来的配置,别人可以直接使用。

做游戏的代码,一定要时刻准备随时让别人看,因为随时要改。


2.typedef相当于重新声明了一个结构,所以开头应该大写。

typedef std::map<int, GodWingJinJieConfigPtr> mapCGodWingJinJieInfoList;
typedef std::map<int, GodWingExpSkinPtr> mapCGodWingSkinInfoList;
typedef std::map<int, GodWingSkillPtr> mapCGodWingSkillInfoList;
typedef std::map<int, Message::Game::SPlayerGodWingCritPrompt> mapJinJieExpInfoList;
开头的m都应该改成M.

另外,对于这种别名,最好起名字的时候不要带有具体功能意思的名字,毕竟其他地方可以重用的。


3.对于拼音的命名,一个词的开头才大写。是词,不是字。

GodWingJinJieConfigPtr
应该是

GodWingJinjieConfigPtr
如果要用拼音的话。


4.除数不能为0

bool CGodWingConfigManager::getBlessingValue(int jinJieLevel, int blessingValue, bool& isLevelUp, Message::Game::SPlayerGodWingCritPrompt& cirtInfo)
{
	MapCGodWingJinjieConfig::const_iterator iter1 = _jinJieMap.find(jinJieLevel);
	if(_jinJieMap.end() == iter1)
	{
		return false;
	}
	if (iter1->second->blessValue <= 0)
	{
		return false;//这里是我加的。
	}
	//获取当前经验百分比
	int blessPercent = blessingValue * 10000 / iter1->second->blessValue;//load配置的地方没检测,函数内也没有检测

	//判断是否可以直接进阶
	int rand = Common::CUtil::myRand(1, 10000);
	for(DictIntInt::const_reverse_iterator iter2 = _jinjieLevelUpMap.rbegin();
		_jinjieLevelUpMap.rend() != iter2;
		++iter2)
	{

5.函数不要用大写开头,类名,结构名都要用大写开头。属性不要用大写。

interface IGodWing
		{
			/*
			* 神翼培养接口
			* @param type 1激活神翼系统,2神翼加经验
			* @param amount 使用数量
			* @exception --失败返回异常 使用异常代码
			*/
			["ami","amd"] void GodWingLevelUp(int type, int amount);//大写了
			
			/*
			* 神翼进阶接口
			* @param autoBuy 是否自动买 0不自动,1自动
			* @param result 返回进阶结果 1进阶成功,2进阶失败
			* @param jinJieValue 返回进阶值
			* @exception --失败返回异常 使用异常代码
			*/
			["ami","amd"] void GodWingJinJie(int autoBuy, out int result, out int jinJieValue);//大写了
			
			/*
			* 打开神翼系统接口
			*/
			["ami","amd"] void OpenGodWing();//<span style="font-family: Arial, Helvetica, sans-serif;">大写了</span>

			
			/*
			* 领取返还进阶石
			*/
			["ami","amd"] void getJinJieReward();
			
居然还写到cdl里面了。。。。。。

6.保存数据库的时候如非必要,都是延时保存。immediatelyUpdate是立即更新的意思,一般使用changeFlag。

//保存数据库
	getSaveInfo(ETPlayerGodWing).immediatelyUpdate = true;
	save(false);

7.使用json,有好处也有坏处。好处是比较方便,节省字段。

但是坏处也很明显,

1.多了很多额外的运算

2.极大增加调试的时候查看内存

3.因为不需要声明,所以也就不知道具体有多少字段在里面。

所以一般情况下,

1.json只是用来节省字段,保存数据库的时候才转化一下。

2.对于一些大的系统json的key值放在Common/Config中

3.对于一些小的地方,只是简单一个字符串就行。

4.不保存复杂的结构,太复杂的要考虑新建一个表。

5.如果实在要保存复杂的内容,先转成string,然后保存到json中,登陆的时候,转成各自的结构,要保存的时候才转成字符串。

6.key中要写明是哪个数据类型,value一般只是基础数据类型。

总结得不好,其实我觉得最大问题是,根本没人知道你里面保存的是什么。

bool GateApp::CGodWingManager::isSkinExist(int skinCode, int flag)
{
	Json::Value json;
	json.parse(_playerGodWing.skinJsStr);

	if(flag)
	{
		for(Json::Value::iterator iter = json[ALL_SKIN_KEY].begin(); json[ALL_SKIN_KEY].end() != iter; ++iter)
		{
			if((*iter).isInt())
			{
				if(skinCode == (*iter).asInt())
				{
					return true;
				}
			}
		}
	}
	else
	{
		for(Json::Value::iterator iter = json[LIMIT_SKIN_KEY].begin(); json[LIMIT_SKIN_KEY].end() != iter; ++iter)
		{
			if(!iter.key().isNull())
			{
				std::string temp = iter.key().asString();
				if(skinCode == atoi(temp.c_str()))
				{
					return true;
				}
			}
		}
	}

	return false;
}
像这种以后不要再用了。

8.playerExtend和playerExtend2保存

	CPlayerHelper::updatePlayerExtend(gateEntity, player);//用这个
	player->getSaveInfo(ETPlayerExtend).changeFlag = true;//不要这样写

9.自己写的功能,要每一句代码都调试过才行。

void GateApp::CItemExtend::useProp( CGateEntityPtr gateEntity, 
	CPlayerPtr player,
	::GateApp::Bag::CBagPtr bag,
	const CPlayerItemPtr& playerItem,
	Common::CBaseItemPtr item,
	int useAmount,
	const ::Message::Public::SeqString& values
								   )
{

	switch( item->getTItem().type )
	{

	case EPropMountDan:
		{
			CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);
		}
		break;
	case EPropGodWingDan:
		{
			CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);//这里明显是坐骑的丹药。。。
		}
		break; 
我们做游戏的流程,测试人员只是负责做黑盒测试,因为非技术人员的员工,关注点一般都在客户端。而服务端的bug除非很明显,当然这个是很明显的。否则是测不到的,很多情况下要靠自己的编码习惯来规避一些低级错误。

10.商品价格必须从t_shop_shell中获得,

			//判断元宝是否足够
			player->enoughMoneyException(EPriceUnitEMoney, cost * oweAmount, updateCode);//商品价格和单位必须从t_shop_shell中获得,

			//购买道具
			CShopManager::instance()->buyItem(gateEntity, SHOP_CODE_AUTO_BUY, itemCode, oweAmount, updateCode);//这里根本不需要购买道具,而是在下面直接扣钱
不能自己找个地方配置。

使用商品的时候必须使用t_shop_shell的unit和amount。

商店打折活动肯定会有的。这个必须要注意。


11.对于bool的内容,做判断的时候不要再==true或者==false了。

	tlogGodWing.oldLevel = (true == flag) ? playerGodWing.jinJieLevel - 1 : playerGodWing.jinJieLevel;
	tlogGodWing.newLevel = playerGodWing.jinJieLevel;
	tlogGodWing.newExp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;
	tlogGodWing.oldExp = oldBless;
	tlogGodWing.addExp = (true == isLevelUp) ? totalExp - oldBless : exp;


12.在一般的消耗某种道具,然后获得某种属性的流程中,要先判断所有可能抛错误码的情况,然后再去执行扣除道具,最后是获得东西。

按具体的例子来说吧:

在神翼进阶的流程中,先判断了是否有当前配置,然后根据道具为空判断是否为最大阶数。其实是没必要的。下一个阶的配置是无论如何都要获取的。

	//获取进阶信息
	CGodWingJinjieConfigPtr jinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel); 
	if (!jinjieConfig)
	{
		CErrorCodeManager::throwException("Public_GameConfigDataError");
	}

	//判断是否最高阶数
	if (0 == jinjieConfig->jiePropNum)//不消耗道具,是最大阶
	{
		CErrorCodeManager::throwException("Public_jinJieMaxLevel");
	}
这是后面,消耗道具之后,再去获取下一阶的配置。如果这里出现问题,之前计算的内容,都白费了,而且还白扣钱了。
	CGodWingJinjieConfigPtr nextJinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel + 1);
	if (!nextJinjieConfig)
	{
		CErrorCodeManager::throwException("Public_jinJieMaxLevel");
	}
这里可以考虑一个问题。在程序运行过程中,极小概率出现意想不到的情况,是先扣除玩家道具呢?还是先给玩家属性?

按理说,玩家处于弱势,扣了钱,不给属性,非常亏,一怒之下可能都不想玩了。好像我们应该优先为玩家考虑的样子。

但是实际情况是,市面上存在各种各样的外挂以及各种各样想占便宜的玩家。如果玩家发现了bug,而这个bug对自己有利,可以不劳而获,即使是极小概率,他们也会去研究,将这种特殊情况出现的概率大大提高,甚至每次都出现。如果是玩家自己发现的还好,可能他自己偷偷刷bug,我们发现了就处理了,没发现也只是影响一个服的问题。但是如果是被外挂利用了,可能在极短时间内,被无数人疯狂刷,这个时候很可能我们就得回档,即使没这么惨,也得各种补偿,对游戏的生命周期大大影响。而且非常打击正常玩家的信心。

说多了,总之是先扣钱,再给属性。

在写代码的时候要有意识,先把可能抛错的放前面,到了扣物品扣钱的那一步开始,通常就不能再去抛错了。

13.还是那一句,起名是非常重要的,关乎后来者能不能正确看你的代码。

这是神翼进阶的代码,同一个函数内的。

int totalExp = jinjieConfig->blessValue; 
int totalexp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;

14.不要在一个语句里面做太多的计算。

其实我是想说三目运算符?:

tlogGodWing.addExp = ( isLevelUp) ? totalExp - oldBless : exp;
出现这种情况,通常都是上面已经分成很多分支了,定义个addExp,在其中复制就好了。可以代码更加清晰。

15.客户端调用接口的时候,一些不是很紧要的代码,可以放在Response之后。

	GodWingJinJieCB->cdeResponse(isLevelUp, exp);

	//for log
	Message::Db::Tables::TLogGodWing tlogGodWing;
	tlogGodWing.__init();
	tlogGodWing.userName = player->getTPlayer().username;
就像这样,日志的保存,放在response之后。


16.bool不要和int混用

		//判断皮肤是否已拥有,flag为true检测永久卡中是否已拥有该卡,false检测限时卡中是否已拥有该卡
		bool isSkinExist(int skinCode, int flag);
一般情况下是不会有问题的,但是如果出问题的时候,只能呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵了,碰到过,非常难找。尽量不要用默认类型转换。这相当于削弱了C++强类型的属性,变成弱类型,就会像客户端的Lua一样,非常多bug,而且很难修的。

17.检测限时的皮肤的时候,要判断一下时间是否过期了。虽然已经有每分钟检测的定时器,但是出于严谨,还是要检测一下。

bool GateApp::CGodWingManager::isSkinExist(int skinCode, bool isForever)
{ 
	if (isForever)
	{
		for (SeqInt::iterator iter = _foreverSkins.begin();
			iter != _foreverSkins.end();
			iter ++)
		{
			if (skinCode == *iter)
			{
				return true;
			}
		} 
	}
	else
	{
		DictIntInt::iterator iter = _limitSkinMap.find(skinCode);
		if (iter != _limitSkinMap.end())
		{
			cdf::CDateTime now;
			if (iter->second > now.getTotalSecond())//-----这个检测是我加的,本来没有的----------------
			{
				return true;//还没过期
			}
		} 
	}

	return false;
}


18.对于穿戴皮肤,传0过来就是脱掉了。没必要弄多一个接口。语义上也是一样的,分出来很多余。

void
::Message::Game::IGodWingImpl::setGodWingSkin_async(const ::Message::Game::AMD_IGodWing_setGodWingSkinPtr& setGodWingSkinCB,
int skinCode,
const ::cde::CContext& context)
下面这个是多余的。

void
::Message::Game::IGodWingImpl::takeOffGodWingSkin_async(const ::Message::Game::AMD_IGodWing_takeOffGodWingSkinPtr& takeOffGodWingSkinCB,
const ::cde::CContext& context)

19.有些函数里面会抛异常,在外面就不用再去抛了。

GateApp::Bag::CBagPtr roleBag = CBagHelper::getBag(gateEntity, EPlayerItemPosTypeRole);
if(!roleBag)
{
	CErrorCodeManager::throwException("ErrorGate_BagNotExist");
}
实际上函数里面已经抛异常了。。。

GateApp::Bag::CBagPtr GateApp::CBagHelper::getBag( 
	const CGateEntityPtr& gateEntity,
	const ::Message::Public::EPlayerItemPosType& type, 
	bool isException /*= true*/ )
{
	::GateApp::Bag::CBagSystemPtr bagSystem = getBagSystem(gateEntity, isException);
	if (bagSystem == NULL)
	{
		return NULL;
	}

	GateApp::Bag::CBagPtr bag = bagSystem->getBag( type );
	if ( ! bag && isException )
	{
		Common::CErrorCodeManager::throwException( "ErrorGate_BagNotExist" );
	}
	return bag;
}

20.对于某些属性,过了某个时间失效的情况。设个定时器,到了这个时间点,就把所有的都改掉。这是策划的思想,而不是程序员的做法。

int CPlayerGodWingTimer::handleTimeout(const cdf::CDateTime ¤tTime, const void *act)
{

	CGateEntityPtr gateEntity = CGateEntityManager::instance()->findGateEntity( _entityId );
	if (!gateEntity)
	{
		return 0;
	}

	CPlayerPtr player = CPlayerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER));
	if(!player)
	{
		return 0;
	}

	CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));
	if(!godWingManager)
	{
		return 0;
	}

	//等级判断
	if (player->isFunctionOpen("GodWing"))
	{
		//清空限时祝福值
		godWingManager->checkLimitBless(player);
	}

	gateEntity->removeTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER);

	CPlayerGodWingTimerPtr timer = new CPlayerGodWingTimer();
	timer->_entityId = _entityId;
	cdf::CReactor::instance()->schedule(timer, NULL,
		cdf::CInterval(1, 5, 0, 0, 0), 0);
	gateEntity->addTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER, timer);

	return 0;
}

定时器是个很方便的东西,但是用多了可能造成负担。而且这种限定时间,对所有玩家都有效的情况。可能会造成集中时间爆发大量计算。那个时刻会很卡。

而且五点钟,应该是很少人的时候,系统一般都进行一些比较耗时的操作,更新升级维护备份什么的。说多了,就是尽量不要用定时器。

正确的做法是在用到的时候,检测一下,发现时间过时了,就更新一下。

void
::Message::Game::IGodWingImpl::godWingJinJie_async(const ::Message::Game::AMD_IGodWing_godWingJinJiePtr& GodWingJinJieCB,
	int autoBuy,
	const ::cde::CContext& context)
{
	CGateEntityPtr gateEntity;
	CPlayerPtr player;
	CGateHelper::getPlayerAndEntity(context, gateEntity, player);

	//参数检查
	CCheckInput::validValue(autoBuy, 0, 1);

	CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));
	if(!godWingManager)
	{
		CErrorCodeManager::throwException("Public_NullPointer");
	}
	godWingManager->checkLimitBless(player);

makeClientGodWingInfo函数里面也检测一下就够了。


21.虽然我们做的不是网站,但是也要有MVC的概念。发送给客户端的信息应该相对独立。不能从发送给客户端的消息中触发属性的计算。假如没发到呢,难道属性就不用算了?

void GateApp::CGodWingManager::makeClientGodWingInfo(Message::Game::SPlayerGodWingInfo& playerGodWing, CPlayerPtr& player, CGateEntityPtr& gateEntity)
{
	。。。此处省略。。。

	if(_isNeedcalc)
	{
		getGodWingAllAttribute(gateEntity, player, playerGodWing.attributeInfo);
		getGodWingBaseAttribute(playerGodWing.baseAttribute);

		_allAttribute = playerGodWing.attributeInfo;
		_baseAttribute = playerGodWing.baseAttribute;

		_isNeedcalc = false;
	}
	else
	{
		playerGodWing.attributeInfo = _allAttribute;
		playerGodWing.baseAttribute = _baseAttribute;
	}
}

22.过度依赖json,写出来的代码,调试的时候根本没法看到内存。

void GateApp::CGodWingManager::getActivedSkinEquipSkills(int skinCode, Message::Public::DictIntInt& skillList)
{
	std::string temp1 = _activeSkinSkillJson[ToStr(skinCode)].asString();
	if(temp1.empty())
	{
		return;
	}

	Json::Value js;
	js.parse(temp1);

	std::string temp2 = js[ACTIVE_EQUIT_SKILL_LIST].asString();
	if(temp2.empty())
	{
		return;
	}

	std::vector<std::string> VecSkill;
	cdf::CStrFun::split(VecSkill, temp2.c_str(), ';');

	for(std::vector<std::string>::const_iterator it = VecSkill.begin(); VecSkill.end() != it; ++it)
	{
		std::vector<std::string> temp;
		cdf::CStrFun::split(temp, it->c_str(), ',');
		if(2 != temp.size())
		{
			continue;
		}

		skillList[atoi(temp[0].c_str())] = atoi(temp[1].c_str());
	}
}
一层套一层的,无法看。如此复杂的结构,应该新建一个表来保存内容。

后台php统计的时候用到json也是很麻烦的,这样最终还是拖慢整个计算的速度。

















转载于:https://www.cnblogs.com/fablegame/p/6119717.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值