通过简单手游打小三,学习手机网游的开发(进入6月,我们的仙剑demo在这个月完成,先插些别的东东)

本文主要是介绍通过简单手游打小三,学习手机网游的开发(进入6月,我们的仙剑demo在这个月完成,先插些别的东东),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        学习手机网游的开发,说实话,网游开发这个话题太大,我也没啥经验,毕业这么多年参与的20多款游戏中,一共只做了3个网游项目,前两个是kjava的手机网游(这个实在是,当时所在单位想法是好的,但现实很残酷,j2me性能不足以满足需要),第三个是as3的webgame(因为种种原因难产),但很遗憾,没一个成功的,因为本人能力的先天不足(我大学主修的不是计算机),不过就通过对九秒开源例子打小三,谈谈网游基本的开发思路吧,打小三是9秒出的一款基于firefly服务器引擎的开源例子(最近9秒很不务正业,说实话,作为一个有自己很好产品线的技术企业,现在不把精力用在推广自己的几个开源的产品上,而是去搞其他产品的推广和培训,我觉得有些不理解),回到我们的话题上来,我们看下打小三是个什么东东,我们先把它运行起来

首先启动服务器:如下面

swordfishx@swordfishx-virtual-machine:~$ cd mzdemo
swordfishx@swordfishx-virtual-machine:~/mzdemo$ ls
app  appmain.py  config.json  game  mzdemo.sql  nbproject  startmaster.py  tool
swordfishx@swordfishx-virtual-machine:~/mzdemo$ python startmaster.py
2015-06-02 11:24:43+0800 [-] Log opened.
2015-06-02 11:24:43+0800 [-] DelaySite starting on 7777
2015-06-02 11:24:43+0800 [-] Starting factory <firefly.web.delayrequest.DelaySite instance at 0xa5f0cac>
2015-06-02 11:24:43+0800 [-] BilateralFactory starting on 8888
2015-06-02 11:24:43+0800 [-] Starting factory <firefly.distributed.root.BilateralFactory instance at 0xa5f826c>
2015-06-02 11:24:53+0800 [-] Log opened.
2015-06-02 11:24:53+0800 [-] game start...
2015-06-02 11:24:53+0800 [-] game pid: 8381
2015-06-02 11:24:54+0800 [-] Log opened.
2015-06-02 11:24:54+0800 [BilateralBroker,0,127.0.0.1] node [game] takeProxy ready
2015-06-02 11:24:54+0800 [-] net start...
2015-06-02 11:24:54+0800 [-] net pid: 8380
2015-06-02 11:24:54+0800 [BilateralBroker,1,127.0.0.1] node [net] takeProxy ready
2015-06-02 11:24:55+0800 [-] Log opened.
2015-06-02 11:24:56+0800 [-] Log opened.
2015-06-02 11:24:56+0800 [-] init_globals()
2015-06-02 11:24:56+0800 [-] init_globals()
2015-06-02 11:24:56+0800 [-] gate start...
2015-06-02 11:24:56+0800 [-] gate pid: 8377
2015-06-02 11:24:56+0800 [-] data start...
2015-06-02 11:24:56+0800 [-] data pid: 8382
2015-06-02 11:24:56+0800 [BilateralBroker,2,127.0.0.1] node [gate] takeProxy ready
2015-06-02 11:24:56+0800 [Broker,client] call method remote_connect on service[single]
2015-06-02 11:24:56+0800 [Broker,client] Starting factory <twisted.spread.pb.PBClientFactory instance at 0xa27950c>
2015-06-02 11:24:56+0800 [Broker,client] call method remote_connect on service[single]
2015-06-02 11:24:56+0800 [Broker,client] Starting factory <twisted.spread.pb.PBClientFactory instance at 0x9d518ec>
2015-06-02 11:24:56+0800 [BilateralBroker,3,127.0.0.1] node [data] takeProxy ready
2015-06-02 11:24:56+0800 [BilateralBroker,0,127.0.0.1] node [game] takeProxy ready
2015-06-02 11:24:56+0800 [BilateralBroker,1,127.0.0.1] node [net] takeProxy ready
打小三的服务器是基于9秒的开源服务器firefly制作的,具体安装搭建方法请自行学习(可以参见我写的无忧跑起烽烟ol,这里就不费篇幅了)。服务端启动完成了,我们来看客户端应该怎么办,打小三的客户端使用了cocos2dx引擎的2.20版本,当然2.23版本我也做了win32版本,可以加9秒交流群去下载,具体编译呢,很简单,我们先看下单机版本

我们不是讲cocos2dx开发,所以具体单机版怎么做的,我就不说了,看下差异

先是单机版AppDelegate.cpp中有这样的代码

bool AppDelegate::applicationDidFinishLaunching() {// initialize directorCCDirector* pDirector = CCDirector::sharedDirector();CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();pDirector->setOpenGLView(pEGLView);// turn on display FPSpDirector->setDisplayStats(true);// set FPS. the default value is 1.0/60 if you don't call thispDirector->setAnimationInterval(1.0 / 60);// create a scene. it's an autorelease object// CCScene *pScene = HelloWorld::scene();CCScene*pscene=GameScene::scene();// runpDirector->runWithScene(pscene);return true;
}
GameScene.cpp中是这样的,
bool GameScene::init(){CCLOG("jkajskas");_gamelayer=GameLayer::create();this->addChild(_gamelayer);//地图类调用_hudLayer=HudLayer::create();this->addChild(_hudLayer);return true;
}  

最后GameLayer.cpp中是这样的

#include "GameLayer.h"
USING_NS_CC;
#include "Robot.h"
#include"GameScene.h"
//--------------------声音
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
//---------------------------------声音
#define random_range(low,high) (rand()%(high-low+1))+low
#define frandom (float)rand()/UINT64_C(0x100000000)
#define frandom_range(low,high) ((high-low)*frandom)+low
//获取当前时间
static float GetCurTime()
{timeval time;gettimeofday(&time,NULL);unsigned long millisecs=(time.tv_sec*1000)+(time.tv_usec)/1000;return (float)millisecs;
}bool GameLayer::init(){this->Soundinit();//SimpleAudioEngine::sharedEngine()->playBackgroundMusic("latin_industries.wav");this->initTiledmap();this->initActionSprite();this->initHero();this->initRobots();this->setTouchEnabled(true);this->scheduleUpdate();return true;
}  
//预加载音乐和音效------------------
void GameLayer::Soundinit(){SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("latin_industries.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit0.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("pd_hit1.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("pd_herodeath.wav");SimpleAudioEngine::sharedEngine()->preloadEffect("pd_botdeath.wav");
}void GameLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent){this->_hero->attack();this->HitCreate();}
void GameLayer::HitCreate(){CCObject *object=NULL;CCARRAY_FOREACH(this->_robots,object){Robot*robot=(Robot*)object;if(robot->_actionState!=kActionStateDead){//fabsf计算浮点绝对值,英雄和敌人在偏差为10的直线上if(fabsf(_hero->getPositionY()-robot->getPositionY())<10){//手的盒子intersectsRect(hit盒子)if(_hero->_attackBox.actual.intersectsRect(robot->_hitBox.actual)){CCLOG("pengzhuang");robot->hurtWithDamage(_hero->_damage);}}}}}//绘制人物动作
void GameLayer::initActionSprite(){CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("mz_sprites.plist");_actors=CCSpriteBatchNode::create("mz_sprites.png");_actors->getTexture()->setAliasTexParameters();//抗锯齿this->addChild(_actors);}//绘制tile地图
void GameLayer::initTiledmap(){_tilemp=CCTMXTiledMap::create("mz_tilemap.tmx");CCObject *obj=NULL;CCARRAY_FOREACH(_tilemp->getChildren(),obj){CCTMXLayer *_child=(CCTMXLayer*)obj;_child->getTexture()->setAliasTexParameters();//关闭抗锯齿}this->addChild(_tilemp);}
//添加英雄
void GameLayer::initHero(){this->_hero=Hero::create();this->_actors->addChild(this->_hero);this->_hero->setPosition(ccp(this->_hero->_centerToSides,80));this->_hero->_desiredPosition=this->_hero->getPosition();this->_hero->idle();}// there's no 'id' in cpp, so we recommend returning the class instance pointer
CCScene* GameLayer::scene(){CCScene*scene=CCScene::create();GameLayer*layer=GameLayer::create();scene->addChild(layer);return scene;
}
//执行 行走动作
void GameLayer::didChangeDirectorTo(SimpleDPad* simpleDPad,CCPoint direction){this->_hero->walkWithDirection(direction);
}
//执行 空闲动作
void GameLayer::isHoldingDirector(SimpleDPad* simpleDPad,CCPoint direction){this->_hero->walkWithDirection(direction);
}//触摸方向键结束调用的函数
void GameLayer::simpleDPadTouchEnded(SimpleDPad* simpleDpad){if(this->_hero->_actionState==kActionStateWalk){this->_hero->idle();}
}
//设置英雄位置
void GameLayer::HeroPosition(){float posX=MIN(this->_tilemp->getMapSize().width*this->_tilemp->getTileSize().width-this->_hero->_centerToSides,MAX(this->_hero->_centerToSides,this->_hero->_desiredPosition.x));float posY=MIN(3*this->_tilemp->getTileSize().height+this->_hero->_centerToBottom,MAX(_hero->_centerToBottom,_hero->_desiredPosition.y));this->_hero->setPosition(ccp(posX,posY));CCSize winSize=CCDirector::sharedDirector()->getWinSize();int x=MAX(_hero->getPositionX(),winSize.width/2);int y=MAX(_hero->getPositionY(),winSize.height/2);x=MIN(x,(this->_tilemp->getMapSize().width*this->_tilemp->getTileSize().width)-winSize.width/2);y=MIN(y,(this->_tilemp->getMapSize().height*this->_tilemp->getTileSize().height)-winSize.height/2);CCPoint actualPosition=ccp(x,y);CCPoint centerOfView=ccp(winSize.width/2,winSize.height/2);CCPoint viewPoint=ccpSub(centerOfView,actualPosition);this->setPosition(viewPoint);}
//设置机器人的位置
void GameLayer::RobotPosition(){//限定机器人只能在地图范围内运动,跟英雄一样的判定CCObject* pObject=NULL;CCARRAY_FOREACH(this->_robots,pObject){Robot* robot=(Robot*)pObject;float posX=MIN(this->_tilemp->getMapSize().width*this->_tilemp->getTileSize().width-robot->_centerToSides,MAX(robot->_centerToSides,robot->_desiredPosition.x));float posY=MIN(3*this->_tilemp->getTileSize().height+robot->_centerToBottom,MAX(robot->_centerToBottom,robot->_desiredPosition.y));robot->setPosition(ccp(posX,posY));}}void GameLayer::update(float dt){this->_hero->updateDesiredPosition(dt);HeroPosition();RobotPosition();ChangeHeroAndRobotZ();this->updateRobots(dt);}
//改变机器人和英雄的z轴顺序
void GameLayer::ChangeHeroAndRobotZ(){CCObject* object=NULL;//_actors获取之后CCARRAY_FOREACH遍历,放入object,之后用reorderChild(新的排序之后的顺序),1000是个足够大的数,不一定要1000CCARRAY_FOREACH(this->_actors->getChildren(),object){this->_actors->reorderChild((CCNode*)object,1000-((ActionSprite*)object)->getPositionY());}}//添加机器人
void GameLayer::initRobots(){this->_robots=CCArray::create();this->_robots->retain();for (int i = 0; i < ADD_ROBOT_COUNT; i++){Robot* robot=Robot::create();this->_actors->addChild(robot);this->_robots->addObject(robot);//加入数组//初始化机器人位置CCSize winSize=CCDirector::sharedDirector()->getWinSize();int minX=winSize.width/2+robot->_centerToSides;int maxX=this->_tilemp->getMapSize().width*this->_tilemp->getTileSize().width-robot->_centerToSides;int minY=robot->_centerToBottom;int maxY=3*this->_tilemp->getTileSize().height+robot->_centerToBottom;robot->setScaleX(-1);robot->setPosition(ccp((rand()%(maxX-minX+1))+minX,(rand()%(maxY-minY+1))+minY));robot->_desiredPosition=robot->getPosition();robot->idle();}}
GameLayer::GameLayer(void)
{this->_hero=NULL;this->_robots=NULL;
}GameLayer::~GameLayer(void)
{
}void GameLayer::updateRobots(float dt){int alive=0;//当前活着的机器人的数量int distanceSQ;//保存两点距离的平方int randomChoice=0;//随机选择CCObject*pObject=NULL;CCARRAY_FOREACH(this->_robots,pObject){Robot*robot=(Robot*)pObject;//因为数组中取出的是CCObject类型robot->updateDesiredPosition(dt);//robot->updateDesiredPosition(dt);//获得每个机器人的位置//当不是死亡状态if(robot->_actionState!=kActionStateDead){alive++;//判断当前时间是否大于抉择时间if(::GetCurTime()>robot->_nextDecisionTime){distanceSQ=ccpDistanceSQ(robot->getPosition(),this->_hero->getPosition());//计算机器人和英雄之间的距离if(distanceSQ<=50*50){robot->_nextDecisionTime=::GetCurTime()+frandom_range(0.1,0.5)*1000;randomChoice=random_range(0,1);//在0,1之间随机选择一个if(randomChoice==0){//执行攻击动作if (this->_hero->getPositionX()>robot->getPositionX()){robot->setScaleX(1.0f);//机器人位置在英雄左边,机器人朝向发生变化}else{robot->setScaleX(-1.0f);}robot->_nextDecisionTime=robot->_nextDecisionTime+frandom_range(0.1,0.5)*2000;robot->attack();//检测是否攻击到英雄if (fabsf(this->_hero->getPositionY()-robot->getPositionY()<10)){//英雄的盒子碰撞到了robot的_attackBoxif(_hero->_hitBox.actual.intersectsRect(robot->_attackBox.actual)){_hero->hurtWithDamage(robot->_damage);//英雄受伤//如果英雄死亡并且这时没有点击重新开始菜单if(_hero->_actionState==kActionStateDead && (((GameScene*)(this->getParent()))->_hudLayer->getActionByTag(537)==NULL)){this->endGame();}}}}else{//执行空闲动作robot->idle();}}else if (distanceSQ<=CCDirector::sharedDirector()->getWinSize().width*CCDirector::sharedDirector()->getWinSize().width){robot->_nextDecisionTime=::GetCurTime()+frandom_range(0.5,1.0)*1000;randomChoice=random_range(0,2);if(randomChoice==0){//机器人执行行走动作//求标准化向量ccpNormalize,ccpSub减法CCPoint moveDirection=ccpNormalize(ccpSub(_hero->getPosition(),robot->getPosition()));robot->walkWithDirection(moveDirection);robot->updateDesiredPosition(dt*20);//更新机器人位置}else{//执行空闲动作robot->idle();}}}}}//如果机器人数量为0,并且没有调用重新开始menuif(alive==0 && ((GameScene*)(this->getParent()))->_hudLayer->getChildByTag(537)==NULL){this->endGame();}}
//结束游戏
void GameLayer::endGame(){CCSize size=CCDirector::sharedDirector()->getVisibleSize();CCLabelTTF*label=CCLabelTTF::create("restart","Arial",40);CCMenuItemLabel*restartItem=CCMenuItemLabel::create(label,this,callfuncO_selector(GameLayer::restartGame));CCMenu*menu=CCMenu::create(restartItem,NULL);menu->setPosition(ccp(size.width/2,size.height/2));menu->setTag(537);if(((GameScene*)(this->getParent()))->_hudLayer !=NULL){//获取到父节点GameScene的指针((GameScene*)(this->getParent()))->_hudLayer->addChild(menu);}}
//重启游戏回调
void GameLayer::restartGame(CCObject* pSender){CCDirector::sharedDirector()->replaceScene((CCScene*)GameScene::create());}
我们通过上面3个代码片段可以看出,单机版的游戏全部逻辑都是在本地也就是GameLayer中实现的,这个跟我们的网络版打小三有什么不同呢?我们贴下工程资源管理

我们可以看到没有需要多余的工具包,就完成了,人物移动,打怪等等操作,现在我们关闭单机版本,开启9秒的网络版本。

我们看下工程

刚刚我们打开的是GameUseTool,现在我们打开beatgirl

我们看下资源管理器是不是跟单机版的很像,只不过在类名前面多了MZ两个字母

的确,那网络版本的实现是不是也跟单机版差不多呢,答案是差不多,不过裁决方式不同,单机版使用的裁决方式是客户端裁决,而网络版使用的服务器端裁决机制,而这个机制是怎么实现的呢?

我们看下网络版的代码

bool AppDelegate::applicationDidFinishLaunching() {// initialize directorCCDirector* pDirector = CCDirector::sharedDirector();CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();pDirector->setOpenGLView(pEGLView);pEGLView->setDesignResolutionSize(960, 640, kResolutionFixedHeight);// turn on display FPSpDirector->setDisplayStats(true);// set FPS. the default value is 1.0/60 if you don't call thispDirector->setAnimationInterval(1.0 / 60);// create a scene. it's an autorelease object
//    CCScene *pScene = HelloWorld::scene();
//    CCScene* pScene = MZGameScene::create();CCScene* pScene = MZStartAnimation::scene();// runpDirector->runWithScene(pScene);return true;
}
跟上面一样,我们也有也看看入口scene实现了什么

跟刚刚有点不同的是,我们进入时多了一个登录页面,那这个登录页面都实现了什么呢,我们看看代码

//
//  MZStartAnimation.cpp
//  mzdemo_client
//
//  Created by stevenLi on 13-12-20.
//
//#include "MZStartAnimation.h"
#include "resource.h"
#include "MZStartGame.h"
#include "MZDataManager.h"
#include "MZDefines.h"
#include "LodingLayer.h"
#include "message.h"bool MZStartAnimation::init(){if (!CCLayer::init())return false;createTheInterface();CCNotificationCenter::sharedNotificationCenter()->addObserver(this, menu_selector(MZStartAnimation::screenAction), "ScreenAction", NULL);return true;
}CCScene* MZStartAnimation::scene(){CCScene* scene = CCScene::create();MZStartAnimation* layer = MZStartAnimation::create();scene->addChild(layer);return scene;
}void MZStartAnimation::onExit(){CCNotificationCenter::sharedNotificationCenter()->removeObserver(this , "ScreenAction");CCLayer::onExit();
}void MZStartAnimation::callBack(CCObject* pSender){std::string str = CCUserDefault::sharedUserDefault()->getStringForKey("NAME");str = "111";if (!str.empty() && str.compare("") != 0) {MZDataManager* dataManager = MZDataManager::sharedDataManager();/* CSCSJson::FastWriter writer;CSCSJson::Value data;*/CSJson::FastWriter writer;CSJson::Value data;data["name"] = str.c_str();std::string CSJson_data = writer.write(data);dataManager->sendMessage(CSJson_data.c_str(), 1000);this->schedule(schedule_selector(MZStartAnimation::receive_1000));_loading = Loading::create();this->addChild(_loading, 5);}
}void MZStartAnimation::receive_1000(float dt)
{MZDataManager* dataManager = MZDataManager::sharedDataManager();Message* msg = (Message *)dataManager->m_dictionary->objectForKey(1000);dataManager->m_dictionary->removeObjectForKey(1000);if (msg) {_loading->removeFromParent();this->unschedule(schedule_selector(MZStartAnimation::receive_1000));char* receive_data = msg->data;/* CSCSJson::Reader reader;CSCSJson::Value root;*/CSJson::Reader reader;CSJson::Value root;if (!reader.parse(receive_data, root)) {return;}//CSCSJson::Value data;CSJson::Value data;data = root["rev_data"];dataManager->setUserID(data["user_id"].asInt());dataManager->setUserInfo(receive_data);CCScene* scene = CCScene::create();MZStartGame* layer = MZStartGame::create();scene->addChild(layer);CCTransitionFade* transitionScene = CCTransitionFade::create(0.1f, scene);CCDirector::sharedDirector()->pushScene(transitionScene);}
}void MZStartAnimation::createTheInterface(){CCSprite* _bgSpr = CCSprite::create(mz_MZStartAnimation_bgImage);_bgSpr->setPosition(CENTER);this->addChild(_bgSpr);CCSprite* _borderSpr = CCSprite::create(mz_MZStartAnimation_Border);_borderSpr->setPosition(ccp(ScreenWidth* 0.5, ScreenHeight* 0.42));this->addChild(_borderSpr, 1);CCMenuItem* item = CCMenuItemImage::create(mz_MZStartAnimation_Login1, mz_MZStartAnimation_Login2, this, menu_selector(MZStartAnimation::callBack));item->setPosition(ccp(ScreenWidth* 0.5, ScreenHeight* 0.22));CCMenu* menu = CCMenu::create(item, NULL);menu->setPosition(CCPointZero);this->addChild(menu, 1);std::string str = CCUserDefault::sharedUserDefault()->getStringForKey("NAME");if (str.c_str() != NULL) {_textField = MZCursorTextField::textFieldWithPlaceHolder("请输入名字", Arial, 20);}else{_textField = MZCursorTextField::textFieldWithPlaceHolder(str.c_str(), Arial, 20);}_textField->setPosition( ccp(ScreenWidth* 0.5, ScreenHeight* 0.37));_textField->setScale(2);_textField->setColor(WhiteColor);_textField->enableStroke(WhiteColor, 0.4f);this->addChild(_textField, 2);
}void MZStartAnimation::screenAction(CCObject* pSender){if (!pSender) {CCFiniteTimeAction* _actionMove = CCMoveTo::create(0.18f,ccp(0, getPositionY()+ 220));this->runAction(CCSequence::create(_actionMove, NULL));}else{CCFiniteTimeAction* _actionMove = CCMoveTo::create(0.18f,ccp(0, getPositionY()- 220));this->runAction(CCSequence::create(_actionMove, NULL));}
}

init()我们看下createTheInterface()这个方法

这个方法中画出了我们这个屏幕中显视的所有东西,包括,背景,按钮,输入框等等,我们不赘述其他的,只关心里面的一个callback,我们看看callback里面实现了什么东西

void MZStartAnimation::callBack(CCObject* pSender){std::string str = CCUserDefault::sharedUserDefault()->getStringForKey("NAME");str = "111";if (!str.empty() && str.compare("") != 0) {MZDataManager* dataManager = MZDataManager::sharedDataManager();/* CSCSJson::FastWriter writer;CSCSJson::Value data;*/CSJson::FastWriter writer;CSJson::Value data;data["name"] = str.c_str();std::string CSJson_data = writer.write(data);dataManager->sendMessage(CSJson_data.c_str(), 1000);this->schedule(schedule_selector(MZStartAnimation::receive_1000));_loading = Loading::create();this->addChild(_loading, 5);}
}

我们可以看到这里面通过json把str字符串做了格式化,然后用sendMessage方法把格式化好的字符串发出,而消息号是1000

在发出消息之后用一个计划来监听receive_1000
当接收到服务器端的指令之后

void MZStartAnimation::receive_1000(float dt)
{MZDataManager* dataManager = MZDataManager::sharedDataManager();Message* msg = (Message *)dataManager->m_dictionary->objectForKey(1000);dataManager->m_dictionary->removeObjectForKey(1000);if (msg) {_loading->removeFromParent();this->unschedule(schedule_selector(MZStartAnimation::receive_1000));char* receive_data = msg->data;/* CSCSJson::Reader reader;CSCSJson::Value root;*/CSJson::Reader reader;CSJson::Value root;if (!reader.parse(receive_data, root)) {return;}//CSCSJson::Value data;CSJson::Value data;data = root["rev_data"];dataManager->setUserID(data["user_id"].asInt());dataManager->setUserInfo(receive_data);CCScene* scene = CCScene::create();MZStartGame* layer = MZStartGame::create();scene->addChild(layer);CCTransitionFade* transitionScene = CCTransitionFade::create(0.1f, scene);CCDirector::sharedDirector()->pushScene(transitionScene);}
在这个方法中用json做解析还原,然后按照服务器指令来做跳转场景的动作,我们可以看到,跟上面的单机版相比,我们的命令不在内存中直接传递,而是经过了客户端格式化发送,服务器接收解析处理发回,客户端接收解析的一个过程。

我们看下这个时候服务器做了什么,我们点账号登录

2015-06-02 12:39:20+0800 [firefly.netconnect.protoc.LiberateFactory] Client 0 login in.[192.168.0.102,54537]
2015-06-02 12:39:20+0800 [LiberateProtocol,0,192.168.0.102] call method call_gate_0 on service[single]
2015-06-02 12:39:21+0800 [BilateralBroker,1,127.0.0.1] call method reply_net_call on service[single]
2015-06-02 12:39:21+0800 [BilateralBroker,1,127.0.0.1] gateapp  ***  reply_net_call()
2015-06-02 12:39:21+0800 [BilateralBroker,1,127.0.0.1] call method gate_localservice_1000 on service[single]
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] ('search user_id, resoult = ', {'money': 200L, 'hitpoints': 1000.0, 'user_id': 106L, 'name': u'111', 'damage': 200.0})
2015-06-02 12:39:23+0800 [LiberateProtocol,0,192.168.0.102] call method call_gate_0 on service[single]
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] call method reply_net_call on service[single]
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] gateapp  ***  reply_net_call()
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] call method gate_localservice_2000 on service[single]
2015-06-02 12:39:23+0800 [LiberateProtocol,0,192.168.0.102] call method call_gate_0 on service[single]
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] call method reply_net_call on service[single]
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] gateapp  ***  reply_net_call()
2015-06-02 12:39:23+0800 [BilateralBroker,1,127.0.0.1] call method gate_localservice_4000 on service[single]
看到了吧,服务器在当有客户端消息的时候做了这些东西,并且调用了
 gate_localservice_1000, gate_localservice_2000, gate_localservice_4000这3个方法,怎么调用的我们后面说,那么2000.和4000的消息在哪呢

MZStartGame* layer = MZStartGame::create();
        scene->addChild(layer);上面有这么两行代码,我们看下这个类

//
//  MZStartGame.cpp
//  mzdemo_client
//
//  Created by stevenLi on 13-12-20.
//
//#include "MZStartGame.h"
#include "resource.h"
#include "MZGameScene.h"
#include "MZDefines.h"
#include "MZDataManager.h"
#include "message.h"
#include "MZTableView.h"
#include "LodingLayer.h"MZStartGame::MZStartGame(){}
MZStartGame::~MZStartGame(){}bool MZStartGame::init(){if (!CCLayer::init()) {return false;}MZDataManager* dataManager = MZDataManager::sharedDataManager();// CSCSJson::FastWriter writer;// CSCSJson::Value value;CSJson::FastWriter writer;CSJson::Value value;value["user_id"] = dataManager->getUserID();std::string CSJson_data = writer.write(value);dataManager->sendMessage(CSJson_data.c_str(), 2000);this->schedule(schedule_selector(MZStartGame::receive_2000));_loading = Loading::create();this->addChild(_loading);CCSprite* _bgSpr = CCSprite::create(mz_MZStartGame_bgImage);_bgSpr->setPosition(CENTER);this->addChild(_bgSpr);return true;
}void MZStartGame::receive_2000(float dt)
{MZDataManager* dataManager = MZDataManager::sharedDataManager();Message* msg = (Message *)dataManager->m_dictionary->objectForKey(2000);dataManager->m_dictionary->removeObjectForKey(2000);if (msg) {_loading->removeFromParent();this->unschedule(schedule_selector(MZStartGame::receive_2000));CCDictionary* dic = CCDictionary::create();dataManager->setDicItems(dic);char* receive_data = msg->data;CCLog("receive_2000 :::\n%s", receive_data);// CSCSJson::Reader reader;// CSCSJson::Value root;CSJson::Reader reader;CSJson::Value root;if (!reader.parse(receive_data, root)) {return;}// CSCSJson::Value data;//data = root["rev_data"];CSJson::Value data;data = root["rev_data"];int count = data.size();for (int i = 0; i < count; i++) {CCLOG("item :::::::::::: %d", data[i]["item_id"].asInt());CCString* strIndex = CCString::createWithFormat("%d",data[i]["item_id"].asInt());dataManager->getDicItems()->setObject(strIndex, strIndex->getCString());}}initLiftButton();initRightButton();
}void MZStartGame::initLiftButton(){CCSprite* kSpr = CCSprite::create(mz_MZStartGame_Kuang);kSpr->setPosition(ccp(ScreenWidth* 0.25, ScreenHeight* 0.65));this->addChild(kSpr, 2);CCSprite* tSpr = CCSprite::create(mz_MZStartGame_Title);tSpr->setPosition(ccp(ScreenWidth* 0.25, ScreenHeight* 0.89));this->addChild(tSpr, 3);CCSprite* dSpr = CCSprite::create(mz_MZStartGame_Down);dSpr->setPosition(ccp(ScreenWidth* 0.25, ScreenHeight* 0.395));this->addChild(dSpr, 3);CCSprite* jbSpr = CCSprite::create(mz_MZStartGame_JinBi);jbSpr->setPosition(ccp(ScreenWidth* 0.15, ScreenHeight* 0.27));jbSpr->setScaleX(0.8f);this->addChild(jbSpr,2);CCSprite* ybSpr = CCSprite::create(mz_MZStartGame_YuanBao);ybSpr->setPosition(ccp(ScreenWidth* 0.35, ScreenHeight* 0.27));ybSpr->setScaleX(0.8f);this->addChild(ybSpr, 2);CCMenuItem* startItem = CCMenuItemImage::create(mz_MZStartGame_Start1, mz_MZStartGame_Start2, this, menu_selector(MZStartGame::startCallBack));startItem->setPosition(ccp(ScreenWidth* 0.25, ScreenHeight* 0.12));CCMenu* menu = CCMenu::create(startItem, NULL);menu->setPosition(CCPointZero);this->addChild(menu, 2);MZTableView* layer = MZTableView::create();this->addChild(layer, 2);
}void MZStartGame::startCallBack(CCObject* pSender){CCScene* scene = CCScene::create();MZGameScene* layer = MZGameScene::create();scene->addChild(layer);CCTransitionFade* transitionScene = CCTransitionFade::create(0.1f, scene);CCDirector::sharedDirector()->pushScene(transitionScene);
}void MZStartGame::initRightButton(){CCSprite* bgSpr = CCSprite::create(mz_MZStartGame_Hero_bgImage);bgSpr->setPosition(ccp(ScreenWidth* 0.75, ScreenHeight* 0.5));this->addChild(bgSpr, 1);MZDataManager* dataManager = MZDataManager::sharedDataManager();CCDictionary* dicItems = dataManager->getDicItems();CCSprite* liftSkill1 = CCSprite::create(mz_MZStartGame_Lift_Skills1_2);liftSkill1->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.86));liftSkill1->setTag(Tag_Lift_Skills1);this->addChild(liftSkill1, 2);liftItem1 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));liftItem1->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.76));liftItem1->setTag(Tag_Lift_Button1);CCMenu* menu1 = CCMenu::create(liftItem1, NULL);menu1->setPosition(CCPointZero);this->addChild(menu1, 3);if (dicItems->valueForKey("10001")->compare("10001") == 0) {CCLog("has 10001");liftItem1->removeFromParent();}CCSprite* liftSkill2 = CCSprite::create(mz_MZStartGame_Lift_Skills2_2);liftSkill2->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.62));liftSkill2->setTag(Tag_Lift_Skills2);this->addChild(liftSkill2, 2);liftItem2 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));liftItem2->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.52));liftItem2->setTag(Tag_Lift_Button2);CCMenu* menu2 = CCMenu::create(liftItem2, NULL);menu2->setPosition(CCPointZero);this->addChild(menu2, 3);if (dicItems->valueForKey("10002")->compare("10002") == 0) {liftItem2->removeFromParent();CCLog("has 10002");}CCSprite* liftSkill3 = CCSprite::create(mz_MZStartGame_Lift_Skills3_2);liftSkill3->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.38));liftSkill3->setTag(Tag_Lift_Skills3);this->addChild(liftSkill3, 2);liftItem3 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));liftItem3->setPosition(ccp(ScreenWidth* 0.59, ScreenHeight* 0.28));liftItem3->setTag(Tag_Lift_Button3);CCMenu* menu3 = CCMenu::create(liftItem3, NULL);menu3->setPosition(CCPointZero);this->addChild(menu3, 3);if (dicItems->valueForKey("10003")->compare("10003") == 0) {liftItem3->removeFromParent();CCLog("has 10003");}CCSprite* rightSkill1 = CCSprite::create(mz_MZStartGame_Right_Skills1_2);rightSkill1->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.86));rightSkill1->setTag(Tag_Right_Skills1);this->addChild(rightSkill1, 2);rightItem1 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));rightItem1->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.76));rightItem1->setTag(Tag_Right_Button1);CCMenu* menu4 = CCMenu::create(rightItem1, NULL);menu4->setPosition(CCPointZero);this->addChild(menu4, 3);if (dicItems->valueForKey("10004")->compare("10004") == 0) {rightItem1->removeFromParent();CCLog("has 10004");}CCSprite* rightSkill2 = CCSprite::create(mz_MZStartGame_Right_Skills2_2);rightSkill2->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.62));rightSkill2->setTag(Tag_Right_Skills2);this->addChild(rightSkill2, 2);rightItem2 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));rightItem2->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.52));rightItem2->setTag(Tag_Right_Button2);CCMenu* menu5 = CCMenu::create(rightItem2, NULL);menu5->setPosition(CCPointZero);this->addChild(menu5, 3);if (dicItems->valueForKey("10005")->compare("10005") == 0) {rightItem2->removeFromParent();CCLog("has 10005");}CCSprite* rightSkill3 = CCSprite::create(mz_MZStartGame_Right_Skills3_2);rightSkill3->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.38));rightSkill3->setTag(Tag_Right_Skills3);this->addChild(rightSkill3, 2);rightItem3 = CCMenuItemImage::create(mz_MZStartGame_Buy1, mz_MZStartGame_Buy2, this, menu_selector(MZStartGame::buyCallBack));rightItem3->setPosition(ccp(ScreenWidth* 0.91, ScreenHeight* 0.28));rightItem3->setTag(Tag_Right_Button3);CCMenu* menu6 = CCMenu::create(rightItem3, NULL);menu6->setPosition(CCPointZero);this->addChild(menu6, 3);if (dicItems->valueForKey("10006")->compare("10006") == 0) {rightItem3->removeFromParent();CCLog("has 10006");}CCSprite* mpSpr = CCSprite::create(mz_MZStartGame_MingPai);mpSpr->setPosition(ccp(ScreenWidth* 0.75, ScreenHeight* 0.92 + 300));mpSpr->setScale(0.85f);this->addChild(mpSpr, 5, 0x6666);std::string str = CCUserDefault::sharedUserDefault()->getStringForKey("NAME");CCLabelTTF* nameLab = CCLabelTTF::create(str.c_str(), Arial, 30);nameLab->setPosition(ccp(ScreenWidth* 0.75, ScreenHeight* 0.92 + 300));nameLab->setColor(GreenColor);nameLab->enableStroke(GreenColor, 1.0f);this->addChild(nameLab, 6, 0x7777);initHero();initLabel();addLabel();this->scheduleOnce(SEL_SCHEDULE(&MZStartGame::moveTo), 0.5f);
}void MZStartGame::moveTo(float dt){CCNode* node1 = this->getChildByTag(0x6666);CCFiniteTimeAction* actionMove1 = CCMoveTo::create(0.7f,ccp(ScreenWidth* 0.75, ScreenHeight* 0.92));CCFiniteTimeAction* actionSpawn1 = CCSpawn::create(actionMove1, NULL);node1->runAction(CCSequence::create(actionSpawn1, NULL));CCNode* node2 = this->getChildByTag(0x7777);CCFiniteTimeAction* actionMove2 = CCMoveTo::create(0.7f,ccp(ScreenWidth* 0.75, ScreenHeight* 0.92));CCFiniteTimeAction* actionSpawn2 = CCSpawn::create(actionMove2, NULL);node2->runAction(CCSequence::create(actionSpawn2, NULL));
}void MZStartGame::initLabel(){MZDataManager* dataManager = MZDataManager::sharedDataManager();char* userInfo = dataManager->getUserInfo();/* CSCSJson::Reader reader;CSCSJson::Value root;CSCSJson::Value data;*/CSJson::Reader reader;CSJson::Value root;CSJson::Value data;int hitpoints = 0;int damage = 0;int money = 0;if (reader.parse(userInfo, root)) {data = root["rev_data"];money = data["money"].asInt();hitpoints = data["hitpoints"].asInt();damage = data["damage"].asInt();}CCLabelTTF* hpLabel = CCLabelTTF::create("血量:   1000   (           )", Arial, 35, CCSizeMake(400, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);hpLabel->setColor(WhiteColor);hpLabel->enableStroke(WhiteColor, 1);hpLabel->setPosition(ccp(ScreenWidth* 0.75, ScreenHeight* 0.18));this->addChild(hpLabel, 2);CCLabelTTF* atLabel = CCLabelTTF::create("攻击:    200    (           )", Arial, 35, CCSizeMake(400, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);atLabel->setColor(WhiteColor);atLabel->enableStroke(WhiteColor, 1);atLabel->setPosition(ccp(ScreenWidth* 0.75, ScreenHeight* 0.09));this->addChild(atLabel, 2);CCString* jbStr = CCString::createWithFormat("%d", money);jbLabel = CCLabelTTF::create(jbStr->getCString(), Arial, 28, CCSizeMake(150, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);jbLabel->setColor(WhiteColor);jbLabel->enableStroke(WhiteColor, 1);jbLabel->setPosition(ccp(ScreenWidth* 0.17, ScreenHeight* 0.267));this->addChild(jbLabel, 4);CCString* ybStr = CCString::createWithFormat("0");CCLabelTTF* ybLabel = CCLabelTTF::create(ybStr->getCString(), Arial, 28, CCSizeMake(150, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);ybLabel->setColor(WhiteColor);ybLabel->enableStroke(WhiteColor, 1);ybLabel->setPosition(ccp(ScreenWidth* 0.37, ScreenHeight* 0.267));this->addChild(ybLabel, 4);
}void MZStartGame::initHero(){CCSpriteFrameCache* cache = CCSpriteFrameCache::sharedSpriteFrameCache();cache->addSpriteFramesWithFile("mz_hero.plist", "mz_hero.png");heroSpr = CCSprite::createWithSpriteFrameName("mz_hero0.png");heroSpr->setPosition(ccp(ScreenWidth* 0.638, ScreenHeight* 0.55));this->addChild(heroSpr, 5);CCArray *fileName = CCArray::createWithCapacity(3);for (int i = 0; i < 4; i++){CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("mz_hero%d.png", i)->getCString());fileName->addObject(frame);}CCAnimation *hurtAnimation = CCAnimation::createWithSpriteFrames(fileName, float(2 / 12.0));heroSpr->runAction(CCRepeatForever::create(CCSequence::create( CCAnimate::create(hurtAnimation), NULL)));
}void MZStartGame::buyCallBack(CCObject* pSender){CCMenuItem* item = (CCMenuItem* )pSender;int itemID = 0;switch (item->getTag()) {case Tag_Lift_Button1:{CCLog("购买左技能1");itemID = 10001;}break;case Tag_Lift_Button2:{CCLog("购买左技能2");itemID = 10002;}break;case Tag_Lift_Button3:{CCLog("购买左技能3");itemID = 10003;}break;case Tag_Right_Button1:{CCLog("购买右技能1");itemID = 10004;}break;case Tag_Right_Button2:{CCLog("购买右技能2");itemID = 10005;}break;case Tag_Right_Button3:{CCLog("购买右技能3");itemID = 10006;}break;default:break;}if (itemID != 0) {MZDataManager* dataManager = MZDataManager::sharedDataManager();/*CSCSJson::FastWriter writer;CSCSJson::Value data;*/CSJson::FastWriter writer;CSJson::Value data;data["user_id"] = dataManager->getUserID();data["item_id"] = itemID;std::string CSJson_data = writer.write(data);dataManager->sendMessage(CSJson_data.c_str(), 3000);this->schedule(schedule_selector(MZStartGame::receive_3000));_loading = Loading::create();this->addChild(_loading, 10);}
}void MZStartGame::receive_3000(float dt)
{MZDataManager* dataManager = MZDataManager::sharedDataManager();Message* msg = (Message* )dataManager->m_dictionary->objectForKey(3000);dataManager->m_dictionary->removeObjectForKey(3000);if (msg) {_loading->removeFromParent();this->unschedule(schedule_selector(MZStartGame::receive_3000));char* receive_data = msg->data;CCLog("receive_3000 :::\n%s", receive_data);/*  CSCSJson::Reader reader;CSCSJson::Value root;*/CSJson::Reader reader;CSJson::Value root;if (!reader.parse(receive_data, root)) {return;}//CSCSJson::Value data;CSJson::Value data;data = root["rev_data"];dataManager->setUserInfo(receive_data);if (root["state_code"].asInt() != 0) {            return;}else {int itemID = root["item_id"].asInt();switch (itemID) {case 10001:{liftItem1->removeFromParent();}break;case 10002:{liftItem2->removeFromParent();}break;case 10003:{CCLog("购买左技能3");liftItem3->removeFromParent();}break;case 10004:{rightItem1->removeFromParent();}break;case 10005:{rightItem2->removeFromParent();}break;case 10006:{rightItem3->removeFromParent();}break;default:break;}}CCString* strJB = CCString::createWithFormat("%d", data["money"].asInt());jbLabel->setString(strJB->getCString());CCString* strHP = CCString::createWithFormat("+%d", data["hitpoints"].asInt() - 1000);hpLabel->setString(strHP->getCString());CCString* strAK = CCString::createWithFormat("+%d", data["damage"].asInt() - 200);atLabel->setString(strAK->getCString());}
}void MZStartGame::addLabel(){MZDataManager* dataManager = MZDataManager::sharedDataManager();char* userInfo = dataManager->getUserInfo();/*  CSCSJson::Reader reader;CSCSJson::Value root;CSCSJson::Value data;*/CSJson::Reader reader;CSJson::Value root;CSJson::Value data;int hitpoints = 0;int damage = 0;int money = 0;if (reader.parse(userInfo, root)) {data = root["rev_data"];money = data["money"].asInt();hitpoints = data["hitpoints"].asInt();damage = data["damage"].asInt();}CCString* str1 = CCString::createWithFormat("+%d", hitpoints - 1000);hpLabel = CCLabelTTF::create(str1->getCString(), Arial, 35, CCSizeMake(400, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);hpLabel->setColor(GreenColor);hpLabel->enableStroke(GreenColor, 1);hpLabel->setPosition(ccp(ScreenWidth* 0.86, ScreenHeight* 0.175));this->addChild(hpLabel, 2);CCString* str2 = CCString::createWithFormat("+%d", damage - 200);atLabel = CCLabelTTF::create(str2->getCString(), Arial, 35, CCSizeMake(400, 30), kCCTextAlignmentCenter, kCCVerticalTextAlignmentCenter);atLabel->setColor(GreenColor);atLabel->enableStroke(GreenColor, 1);atLabel->setPosition(ccp(ScreenWidth* 0.86, ScreenHeight* 0.085));this->addChild(atLabel, 2);
}
我们继续点击开始游戏

2015-06-02 12:42:51+0800 [LiberateProtocol,0,192.168.0.102] call method call_gate_0 on service[single]
2015-06-02 12:42:51+0800 [BilateralBroker,1,127.0.0.1] call method reply_net_call on service[single]
2015-06-02 12:42:51+0800 [BilateralBroker,1,127.0.0.1] gateapp  ***  reply_net_call()
2015-06-02 12:42:51+0800 [BilateralBroker,1,127.0.0.1] call method gate_localservice_3000 on service[single]

我们可以看到又多了这么个gate_localservice_3000的方法,我们看下游戏画面


void MZStartGame::startCallBack(CCObject* pSender){CCScene* scene = CCScene::create();MZGameScene* layer = MZGameScene::create();scene->addChild(layer);CCTransitionFade* transitionScene = CCTransitionFade::create(0.1f, scene);CCDirector::sharedDirector()->pushScene(transitionScene);
}

通过这个回调函数,我们转到了

//
//  MZGameScene.cpp
//  mzdemo_client
//
//  Created by stevenLi on 13-12-20.
//
//#include "MZGameScene.h"
#include "MZDataManager.h"using namespace cocos2d;MZGameScene::MZGameScene(void)
{_gameLayer = NULL;_hudLayer = NULL;
}MZGameScene::~MZGameScene(void)
{
}bool MZGameScene::init()
{bool bRet = false;do {CC_BREAK_IF(!CCScene::init());MZDataManager::sharedDataManager();_gameLayer = MZGameLayer::create();this->addChild(_gameLayer, 0);_hudLayer = MZHudLayer::create();this->addChild(_hudLayer, 1);_hudLayer->getDPad()->setDelegate(_gameLayer);_gameLayer->setMZHud(_hudLayer);bRet = true;} while (0);return bRet;
}
可以比较下跟上面的单机的gamescene是不是一样呢,其实我们实现这些只是把逻辑放到了服务器来完成,而我们通过本地发送和接收消息字符串来告诉服务器我们下一步要做什么,然后服务器听我们的命令传达指令

我们看下,单机c++的通用网络模块都包含哪些东西

其实就是这个网络通用包,其实我们不需要知道它底层是怎么实现的,只要知道发送,接收原理和方法就可以了,

这是客户端,我们看下服务器端是怎么实现的,关掉vs,和服务器,看看代码,

对了,忘了说了,我在9秒群里发win32版本的时候,有些小伙伴加载了我的测试好的代码,却出了很奇怪的一个错误,这个是因为使用上面的c++通过模块需要使用win自带的api,只要在链接器里面添加ws2_32.lib就可以了如图,

好了看服务器。我们为方便打开eclipse

插件我们选Pydev

我们关闭其他的,只看mzdemo,我们看看服务器是怎么运行的

打开config.json

{
"master":{"rootport":8888,"webport":7777},
"servers":{"gate":{"rootport":11111,"name":"gate","app":"app.gateapp","db":true,"mem":true,"log":"app/logs/gate.log"},"net":{"netport":22222,"remoteport":[{"rootport":11111,"rootname":"gate"}],"name":"net","app":"app.netapp","log":"app/logs/net.log"},"game":{"remoteport":[{"rootport":11111,"rootname":"gate"}],"name":"game","app":"app.gameapp","log":"app/logs/game.log"},"data":{"name":"data","app":"app.dataapp","db":true,"mem":true,"log":"app/logs/data.log"}
},
"db":{
"host":"127.0.0.1",
"user":"root",
"passwd":"1234",
"port":3306,
"db":"mzdemo",
"charset":"utf8"
},
"memcached":{"urls":["127.0.0.1:11211"],"hostname":"mzdemo"
}
}
看配置文件,当服务器启动时,servers中会启动gate,net,game,data,这些服务,分别通过一个python文件来转给服务器源码,我们以gate为例

app.gateapp,我们看下打开app文件夹下的gateapp.py

#coding:utf8from firefly.server.globalobject import GlobalObject, rootserviceHandle
from gate.gatelocalservice import gateLocalService# @rootserviceHandle
def gate_side(conn, data):print("gateapp  ***  send_to_game1()")d = GlobalObject().root.callChild("game1", "game1_side", conn, data)
#     d = GlobalObject().remote["game1"].callRemote("game1_side", conn, data)return d
#end def@rootserviceHandle
def reply_net_call(targetKey, clientID, requestData):print("gateapp  ***  reply_net_call()")if gateLocalService._targets.has_key(targetKey):return gateLocalService.callTarget(targetKey, clientID, requestData)else:print("not found the target with key:%s" % targetKey)
#end def
看下firefly api

Firefly框架参考

在游戏服务器端,往往需要处理大量的各种各样的任务,每一项任务所需的系统资源也可能不同。而这些复杂的任务只用一个单独的服务器进程是很难支撑和管理起来的。所以,游戏服务器端的开发者往往需要花费大量的时间精力在诸如服务器类型的划分,进程数量的分配,以及这些进程的维护,进程间的通讯,请求的路由等等这些底层的问题上。而Firefly完全可以完成这些重复而繁琐的工作,从而将上层的游戏开发者解放出来,把精力更多的放在游戏逻辑的实现上面。

一、Firefly特性

1、采用单线程多进程架构,支持自定义的分布式架构。

2、方便的服务器扩展机制,可快速扩展服务器类型和数量

3、与客户端采用TCP长连接,无需考虑粘包等问题。

二、Firefly思路

一个最基本的服务器就是一个在不停运行着的应用程序。在分布式游戏服务器中,我们需要的服务器具有的功能有,监听客户端的连接,监听其他服务进程的消息,连接其他的服务进程,有些需要有数据库连接和缓存服务。如图

net connect 做客户端连接,root监听其他服务进程消息,node连接其他服务进程,db数据库,cache缓存。是否需要监听客户端连接,是否监听其他服务进程消息等这是都是可以在config.json中进行配置。包括各个服务器的名称以及各个服务器之间的连接关系。这样就可以自定义出自己的分布式架构。

三、Firefly结构

从功能职责上来看,Firefly框架结构如下图所示:


    1、management, firefly 是个多进程、分布式的游戏服务器。因此各游戏server(进程)的管理和扩展是firefly很重要的部分,框架通过抽象使服务器的扩展非常容易。

2、Network,客户端连接通信、server进程间的通信等构成了整个游戏框架的脉络,所有游戏流程都构建在这个脉络上。与客户端的通信采用的是请求/回应式的,所有收到的客户端的请求,服务端都会给出相应的回应,服务端也能主动的推送,广播给客户端消息。这些请求是基于指令号的请求。(例如定义101为登陆指令)server进程之间的通信时采用的异步回调的方式,这样就减少了的进程间通过网络通信中的时间消耗。

3、Data, 数据处理是网游的重要部分。在网游有大量的数据需要存储,需要更新,这使得数据库的读写效率成为服务器的最大的性能瓶颈。firefly的db处理能够将数据库表中的数据缓存到memcache中并能以对象的形式进行调用相应的对象方法对数据进行操作。可以在不同的进程中通过实例化相同的名称的缓存实例,得到同步的数据。并能将缓存对象中的数据写回数据库中。



1.从config.json中读取配置数据

2.根据配置中定义的服务端的架构,启动相应的服务进程。并建立节点之间的连接。有配置数据库的,实例化数据库连接池。有配置memcached的,建立memcached的连接。

3.根据配置相应的的进程启动的入口模块。

回到上面

from gate.gatelocalservice import gateLocalService这句

还记得上面的gateLocalService_1000,gateLocalService_2000,gateLocalService_3000吗?我们看下它里面有什么

#coding:utf8from firefly.utils.services import CommandService
from twisted.internet import defer
from twisted.python import log
import jsonfrom ..data.JSONEncoder import CJsonEncoder
from ..data import datautilsfrom ..data import commondataclass GateLocalService(CommandService):def callTargetSingle(self,targetKey,*args,**kw):'''call Target by Single@param conn: client connection@param targetKey: target ID@param data: client data'''self._lock.acquire()try:target = self.getTarget(targetKey)if not target:log.err('the command '+str(targetKey)+' not Found on service')return Noneif targetKey not in self.unDisplay:log.msg("call method %s on service[single]"%target.__name__)defer_data = target(targetKey,*args,**kw)if not defer_data:return Noneif isinstance(defer_data,defer.Deferred):return defer_datad = defer.Deferred()d.callback(defer_data)finally:self._lock.release()return d#end def#end classgateLocalService = GateLocalService("gateLocalService")def gate_localservice_handle(target):gateLocalService.mapTarget(target)
#end def# 用户登陆
@gate_localservice_handle
def gate_localservice_1000(targetKey, clientID, requestData):response = {}response[commondata.rev_data] = {}argument = json.loads(requestData)name = argument.get("name")resoult = datautils.check_user(name)if not resoult:resoult = datautils.create_mcuser(name)response[commondata.rev_data] = resoultelse:response[commondata.rev_data] = resoultresponse[commondata.command_id] = targetKeyreturn json.dumps(response, cls = CJsonEncoder)
#end def# 所有道具
@gate_localservice_handle
def gate_localservice_2000(targetKey, clientID, requestData):argument = json.loads(requestData)userId = argument.get("user_id")response = {}resoult = datautils.get_user_item(userId)if resoult:response[commondata.state_code] = 0response[commondata.rev_data] = resoultelse:response[commondata.state_code] = 1response[commondata.rev_data] = {}response[commondata.command_id] = targetKeyreturn json.dumps(response, cls = CJsonEncoder)
#end def# 买道具
@gate_localservice_handle
def gate_localservice_3000(targetKey, clientID, requestData):argument = json.loads(requestData)user_id = argument.get("user_id")item_id = argument.get("item_id")response = {}response[commondata.rev_data] = {}resoult = datautils.buy_item(user_id, item_id)if resoult:response[commondata.state_code] = 0else:response[commondata.state_code] = 1response[commondata.rev_data] = resoultresponse["item_id"] = item_idresponse[commondata.command_id] = targetKeyreturn json.dumps(response, cls = CJsonEncoder)
#end def# 排名
@gate_localservice_handle
def gate_localservice_4000(targetKey, clientID, requestData):argument = json.loads(requestData)userId = argument.get("user_id")response = {}resoult = datautils.get_user_ranking(userId)if resoult:response[commondata.state_code] = 0response[commondata.rev_data] = resoultelse:response[commondata.state_code] = 1response[commondata.rev_data] = {}response[commondata.command_id] = targetKeyreturn json.dumps(response, cls = CJsonEncoder)
#end def# 上传战斗结果
@gate_localservice_handle
def gate_localservice_5000(targetKey, clientID, requestData):argument = json.loads(requestData)userId = argument.get("user_id")count = argument.get("count")response = {}resoult = datautils.set_resoult(userId, count)if resoult:response[commondata.state_code] = 0response[commondata.rev_data] = resoultelse:response[commondata.state_code] = 1response[commondata.rev_data] = {}response[commondata.command_id] = targetKeyreturn json.dumps(response, cls = CJsonEncoder)
#end def

看到了吧,我们服务器处理客户端的几个消息的方法都在这了,其实网络游戏我们就可以把它看成一个我们本应该在本地做的工作,通过命令发到服务器去做,这个跟我们现在说的云有点相似,上面这些方法处理上面都有一个@gate_localservice_handle修饰,我们可以把它看成是一个方法的参数,最后这个参数被gate_localservice_handle调用,就是target这个东西,由 gateLocalService.mapTarget(target)来处理。

好了,这篇就到这,下篇我们看下我们一直以来做的仙剑demo剧情效果和制作方法,最后放出源码

单机客户端,    访问密码 f3fa

网络客户端,  访问密码 0ba7

服务器端,  访问密码 6107










这篇关于通过简单手游打小三,学习手机网游的开发(进入6月,我们的仙剑demo在这个月完成,先插些别的东东)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/764109

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof