[转载]【深入Cocos2d-x】使用MVC架构搭建游戏Four - 遍历 - 博客园

mikel阅读(1051)

[转载]【深入Cocos2d-x】使用MVC架构搭建游戏Four – 遍历 – 博客园.

喜欢Four这个项目,就赶快在GitHub上Star这个项目吧!

喜欢我的文章,来微博关注我吧:王选易在学C艹

点我下载

项目起源

项目Logo:

下面是该游戏的项目地址,各位想参考源代码的同学可以到我的GitHub上下载该项目的源码。

项目主页

GitHub地址

bug反馈及建议

mvc

我做这个项目的原始目的是实验MVC在游戏中的应用。

Model-View-Controller(MVC)是一种组合设计模式,它体现了一种关注点分离(Separation of concerns,SoC)的思想。MVC主要把逻辑层和表现层进行了解耦,将一个问题划分成了不同的关注点。增强了应用的稳定性,易修改性和易复用性。

MVC经常被使用在Web框架中,包括J2EE,RoR和.Net中都对MVC模型进行了框架层面上的封装,以便程序员可以简单方便地作出结构良好的Web应用。

Cocos2d-x本身并没有提供内置的MVC支持,但是,我们还是可以在游戏中基于MVC架构来设计游戏。在这篇博文中,我将向大家展示一下我是如何使用MVC架构来塔尖Four这个游戏的。

游戏情景

Four这个游戏的创意来自一个叫做走四棋的传统游戏,走四棋规则的详细介绍在这里:走四棋的百度百科

下面我简单谈一下这个面板游戏(board game)的一些特性

  • 一个4行4列的棋盘(Game Board)
  • 棋盘上会有一些“棋子”(Game Piece),每一个方格上只能放一个棋子(Game Piece)。
  • 游戏初始化时,棋盘的上面四格和下面四格分别有4个黑子和四格白子
  • 玩家可以通过话筒使棋子在棋盘上发生移动,从而触发吃子和胜利等事件。
  • 当游戏中出现一个横行或者一个竖列的棋子排布变为两黑一白或者两白一黑时,即可吃掉一子。
  • 当一方(黑或白)的棋子被吃到只剩一个之后,这一方被记为失败

举个例子,下面这幅图即为游戏过程中的一幅图。在下面的游戏过程中,位于(1,0)位置的黑子向左移动到(0,0)的位置后即可吃掉白子。

Cocos2d-x提供的工具

Cocos2d-x有这样一些主要的类,CCSprite,CCLayer,CCScene,CCNotificationCenter。我们会使用这些类进行游戏中MVC架构的搭建,如果你对这些类的作用不熟,请参考我的这篇博文【Cocos2d-x-基础概念】Director Scene Layer and Sprite

我们一般的游戏流程是

  • 通过AppDelegate初始化第一个CCScene。
  • 在第一个CCScene创建多个CCLayer。并控制好CClayer的叠加层次(zOrder)。
  • 在CCLayer中添加各种CCSprite或者CCLabelAtlas或者粒子效果。
  • 在CCLayer层注册触摸事件的监听,并且在CCLayer的实现中写出相应的callback函数,对CCLayer的Child Node进行相应的逻辑处理。

这个过程看起来十分简单,并且可以十分快速地做出游戏。但是其缺陷就在于在CCLayer中我们做了太多的事情。CCLayer同时承担了逻辑层和表示层的任务。不符合我们上文中提到的关注点分离的原则。如果游戏中有较为复杂的状态转换时就捉襟见肘了。

项目的文件目录

下面是该游戏项目的目录结构,我们接下来对这几个文件夹进行分别的讲解:

下面是该项目的一个简单的类图

在类图中,虚线代表的是通过消息机制进行沟通,而在Cocos2d-x中,这种沟通是通过CCNotificationCenter来实现的。

Model(模型)

Model在游戏中代表的是消息驱动的有限状态机,Model会接受Controller层发送的消息,并根据消息来更改自己的内部数据,然后把内部数据改变这一消息发送给View,通知它更新。

Model在Cocos2d-x对应的是哪个类呢?

很遗憾,但是Cocos2d-x并没有提供状态机的feature,所以我们需要自己实现一个Model类,在Model类中,需要自己实现诸如状态转换和消息处理等功能。例如在我的Model类中,我提供了如下接口。

class Model : public CCObject {

public:

// 添加一条状态转换,from-起始状态,msg-接收的消息,to-终结状态,在msg发生时会发生状态转换
Model* addTransition(const string& from, const string& msg, const string& to);

// 检查当前状态机能否发生msg对应的状态转换
bool checkMessage(const string& msg);

// 触发msg对应的状态转换
void onMessage(const string& msg);
void onMessage(const string& msg, CCObject* o);

// 等待某个CCAction结束后发送一条消息。
void waitAction(cocos2d::CCNode* node, cocos2d::CCFiniteTimeAction* action, const string& msg);

// 得到当前状态名称
const char* getState();
};

注意以下几点:

  • 让Model类继承CCObject,是为了与Cocos2d-x自身的内存管理系统一致。
  • onMessage中,我们做的事情就是首先找到当前状态在msg情况下的状态转换。在状态转换后,通过CCNotificationCenter发送一条消息通知游戏中的其他组件更新逻辑。
  • waitAction中,我们是在处理一种异步的状态转换,比如一个棋子在移动时就会经历这样的状态转换start->moving->end,那么在移动结束后,View就要发送一条类似END_MOVE的消息来通知Model更新自己的状态。

Model不会持有View,所以View都是通过消息来获得Model更新的事件的。

我们在编写游戏时,应该先编写Model,然后通过测试来保证Model的正确性。

之后,我们去写View的时候,实际就是对Model这个状态机发送的各种消息的回调函数的编写了。

这样就讲表示层和逻辑层分离开,可以方便地单独测试每个模块,可维护性大大提高。

逻辑数据和实际数据

在编写Model的时候,我们经常会遇到这样的问题,就是Model中的数据是否应当与View中的数据保持一致。

比如:在View层的棋子的位置信息是否应该和Model层的位置信息保持一致?

其实,真实情况就是,这要看Model层的
计算使用那种数据形式更加方便,比如在Model中,我们会把棋盘转化为一个二维数组,这样在AI运算,逻辑判断时,更加有利,所以棋子的逻辑位置和实际位置必然是不同的。

再比如,很多时候游戏中的物理引擎的计量单位是厘米,米。而不是OpenGL中的坐标。这也是为了逻辑运算的方便。

但是有些数值,我们会让它保持一致,比如人物的属性等等。

View(视图)

View在游戏中代表的是Model消息的接受者,在Cocos2d-x中,View一般是指CCLayer的子节点,即CCSprite,CCLabelAtlas,CCMenuItem,Particle System(粒子系统)等等。

它们会重载onEnter函数,在onEnter中注册自己对某个Message的监听。同时在onExit函数中将所有监听清除。(清除监听很重要,否则会出现很可怕的野指针问题)。

Controller(控制器)

Controller在游戏中负责将用户触摸事件转化为逻辑事件(即我们上文中所说的消息),同时要对用户触摸事件中的信息进行正规化,并且通知变更。

Controller在Cocos2d-x中一般用CCLayer
来实现,因为CCLayer自然地继承了CCTouchDelegate这一接口,本身就可以触摸事件,所以我在这个游戏中所有的XXXController都是继承自CCLayer。

Controller另一个很重要的职责,就是创建View和Model,因为Controller相当于Model和View的一个中间层,所以自然Controller会同时持有View和Model的应用,来方便地传递数据。

Protocol(协议)

Protocol中定义了一些Model,View和Controller中共享的数据,

  • Message.h中,就定义了游戏中各种类型的消息,
  • Tag.h中,就定义了游戏中一些Node的Tag,其实Tag这个定义不是很准确,其实这里的Tag指的是唯一标识CCNode的一个ID。
  • ChessboardProrocol.h中,定义了游戏中期盘的宽,搞和一些常用的数据结构。

参考文献

【Blue原创教程】NET破解之第一课(初始化赋值修改)

mikel阅读(1039)

.

有NET软件包(发我百度网盘地址)或者有NET软件官网的论坛短消息我,收集下后续课程的素材
—————————————————————————— 我是分割线 ——————————————————————————————————————–
最近来问我如何破解NET的人比较多,所以打算做一系列的新手教程,仅仅帮助大家入门而已,之后的造诣就看个人了



其实NET的教程论坛搜索一下NET的关键字已经很多了,写得很好很详细啊都,我也是这么看教程入门的罢了(现在依然在入门阶段),为什么没人看呢,为什么不善用搜索呢


好了,没有那么多为什么,就是懒吧,想来懒的人也不会看我的教程,依然是不会破解的,找人问,谁有时间会手把手教你呢,想学习就靠自己努力呗,真心不难的,只要愿意学

—————————————————————————— 我是分割线 ——————————————————————————————————————–

废话结束,开始第一课基础教程,本打算简单介绍一下NET和普通语言的不同,不过想了下,开始就介绍这些枯燥的东西会弄得大家都没有破解的兴趣了,留到以后教程中穿插吧


第一课还是直接实战动手成功破解一个吧,有了信心和动力,枯燥的理论就没那么难以看下去了,第一课会相对比较详细,之后的课程简单的步骤会直接文字略过,不再截图


因为是新手教程,这趟课的工具只需要NETReflector8.1和插件reflexil1.6,最新的应该是8.3和1.7了


NETReflector8.1 链接: http://pan.baidu.com/s/1c0gmOFY 密码: m6r2

reflexil1.6 链接: http://pan.baidu.com/s/1c0gmOFY 密码: m6r2
练手软件(原版和破解版链接: http://pan.baidu.com/s/1eQiQA3o 密码: xfhp

—————————————————————————— 我是分割线 ——————————————————————————————————————–

先打开软件执行一下,看一下未注册的样子

未注册只能三条记录,好的,我们打开我们的工具NETReflector(以后简称RE),打开界面如图

随便选择一个.net framework加载就行了


加载后打开我们的程序,我们的程序加载的样子

此时可以界面点击搜索或者快捷键F3,搜索关键字符串(和OD爆破很像吧,也可以找字符串,而且找到字符串以后比OD更容易找到关键跳,对于一般的NET软件来说)


选第三个是搜索字符串


点击结果查看一下代码

此处注意框中的IF判断条件,如果registered为TRUE,则取消注册按钮,并替换未注册的字符串,再看看别的

这个一看就是注册逻辑,看看注册成功后的程序执行了什么操作,核心的就是把this.prtForm.registered = true;
把REGISTERED赋值TRUE,此处可以改跳转逻辑让异常的注册码也注册成功,不过这次不用这个方法,我们点击REGISTERED,点进去
然后快捷键按CTRL+R或者界面点击,查看哪些地方申明了这个REGISTERED

此处可以看到初始化赋值这个REGISTERED为FALSE,那修改一下初始化为TRUE是不是就可以了呢,我们来尝试一下,选择插件,第一次需要点ADD-INS,选择插件加载,以后就会在插件列表中看到,直接选用就可以了

IL代码中进行修改,ldc.i4.0对应FALSE,要修改为TRUE,改为ldc.i4.1即可

修改完成后UPDATE,然后保存一下

保存后执行一下看看效果

没有限制了,完成,是不是挺简单的?

有问题留言吧


传送门
第一课 http://www.52pojie.cn/thread-248348-1-1.html
第二课 http://www.52pojie.cn/thread-249034-1-1.html
第三课 http://www.52pojie.cn/thread-250626-1-1.html
第四课 http://www.52pojie.cn/thread-250633-1-1.html

[转载].NET EasyUI datebox添加清空功能 - S.Empty - 博客园

mikel阅读(1182)

[转载].NET EasyUI datebox添加清空功能 – S.Empty – 博客园.

前言,前段时间的项目使用EasyUI框架搭建,使用了其自带的一系列组件。但对于datebox,其功能别的不多说,令人蛋疼的是它居然没有 清空功能,这让在搜索区域中摆了日期条件的咋整啊,没办法,既然用了这套前端框架,有问题就得解决,于是想到了自己添加清空功能。

结合在网上找到的一些解决方法以及实际需要,特整理如下。
1.JQuery.easyui.min.js文件修改(版本 JQuery EasyUI 1.3.3)
    (1)第11361行:

    //为datebox定义清空按钮
    $(““).html(opts.cleanText).appendTo(_858);

    (2)第11375行:

    //为datebox清空按钮添加事件
    _858.find(“.datebox-clean”).click(function(){
      $(_853).combo(“setValue”,””).combo(“setText”,””);//设置空值
      $(_853).combo(“hidePanel”);//点击清空按钮之后关闭日期选择面板
    });

    (3)第11445行:

    //定义日期选择面板上的按钮显示文本

    //为清空按钮指定默认显示文本Clean
    currentText:”Today”,cleanText:”Clean”,closeText:”Close”,okText:”Ok”
  2.easyui-lang-zh_CN.js(这是汉化包文件)  

    (1)第40行:

    $.fn.datebox.defaults.cleanText = ‘清空   ‘; //修改datebox清空按钮显示文本,添加 是为了在datetimebox中清空和确定按钮位置隔开
    $.fn.datebox.defaults.okText = ‘   确定’;

    (2)第67行:

    cleanText: $.fn.datebox.defaults.cleanText, //为清空按钮知道默认显示文本

  

  3.easyui.css

    //找到所选easyui主题下文件夹下的easyui.css文件,添加如下样式
    第1528行:
    .datebox-clean,
    .datebox-clean {
      float: center;
    }

  按照以上三步下来,确定无误,保存之后,即可看到效果了,怎么样?可以清空了吧。

  注:不同版本的jQuery.easyui.min.js文件要修改的位置行数可能不一致,请仔细查看。(当前版本: jQuery EasyUI 1.3.3)

[转载]网络爬虫(网络蜘蛛)之网页抓取 - Sonet - 博客园

mikel阅读(930)

[转载]网络爬虫(网络蜘蛛)之网页抓取 – Sonet – 博客园.

现在有越来越多的人热衷于做网络爬虫(网络蜘蛛),也有越来越多的地方需要网络爬虫,比 如搜索引擎、资讯采集、舆情监测等等,诸如此类。网络爬虫涉及到的技术(算法/策略)广而复杂,如网页获取、网页跟踪、网页分析、网页搜索、网页评级和结 构/非结构化数据抽取以及后期更细粒度的数据挖掘等方方面面,对于新手来说,不是一朝一夕便能完全掌握且熟练应用的,对于作者来说,更无法在一篇文章内就 将其说清楚。因此在本篇文章中,我们仅将视线聚焦在网络爬虫的最基础技术——网页抓取方面。

说到网页抓取,往往有两个点是不得不说的,首先是网页编码的识别,另外一个是对网页脚本运行的支持,除此之外,是否支持以POST方式提交请求和支 持自动的cookie管理也是很多人所关注的重要方面。其实Java世界里,已经有很多开源的组件来支持各种各样方式的网页抓取了,包括上面提到的四个重 点,所以说使用Java做网页抓取还是比较容易的。下面,作者将重点介绍其中的六种方式。

 

HttpClient
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器

(5)支持自动的Cookies管理等

Java爬虫开发中应用最多的一种网页获取技术,速度和性能一流,在功能支持方面显得较为底层,不支持JS脚本执行和CSS解析、渲染等准浏览器功能,推荐用于需要快速获取网页而无需解析脚本和CSS的场景。

范例代码如下:

package cn.ysh.studio.crawler.httpclient;import org.apache.http.client.HttpClient;import org.apache.http.client.ResponseHandler;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.BasicResponseHandler;import org.apache.http.impl.client.DefaultHttpClient;/**
 * 基于HtmlClient抓取网页内容
 *
 * @author www.yshjava.cn
 */publicclassHttpClientTest{publicstaticvoid main(String[] args)throwsException{//目标页面String url ="http://www.yshjava.cn";//创建一个默认的HttpClientHttpClient httpclient =newDefaultHttpClient();try{//以get方式请求网页http://www.yshjava.cnHttpGet httpget =newHttpGet(url);//打印请求地址System.out.println("executing request "+ httpget.getURI());//创建响应处理器处理服务器响应内容ResponseHandlerresponseHandler=newBasicResponseHandler();//执行请求并获取结果String responseBody = httpclient.execute(httpget, responseHandler);System.out.println("----------------------------------------");System.out.println(responseBody);System.out.println("----------------------------------------");}finally{//关闭连接管理器
            httpclient.getConnectionManager().shutdown();}}}

 

Jsoup
jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于JQuery的操作方法来取出和操作数据。

网页获取和解析速度飞快,推荐使用。
主要功能如下:
1. 从一个URL,文件或字符串中解析HTML;
2. 使用DOM或CSS选择器来查找、取出数据;
3. 可操作HTML元素、属性、文本;

范例代码如下:

package cn.ysh.studio.crawler.jsoup;import java.io.IOException;import org.jsoup.Jsoup;/**
 * 基于Jsoup抓取网页内容
 * @author www.yshjava.cn
 */publicclassJsoupTest{publicstaticvoid main(String[] args)throwsIOException{//目标页面String url ="http://www.yshjava.cn";//使用Jsoup连接目标页面,并执行请求,获取服务器响应内容String html =Jsoup.connect(url).execute().body();//打印页面内容System.out.println(html);}}

 

HtmlUnit
htmlunit 是一款开源的java 页面分析工具,读取页面后,可以有效的使用htmlunit分析页面上的内容。项目可以模拟浏览器运行,被誉为java浏览器的开源实现。这个没有界面的 浏览器,运行速度也是非常迅速的。采用的是Rhinojs引擎。模拟js运行。

网页获取和解析速度较快,性能较好,推荐用于需要解析网页脚本的应用场景。

范例代码如下:

package cn.ysh.studio.crawler.htmlunit;import com.gargoylesoftware.htmlunit.BrowserVersion;import com.gargoylesoftware.htmlunit.Page;import com.gargoylesoftware.htmlunit.WebClient;/**
 * 基于HtmlUnit抓取网页内容
 *
 * @author www.yshjava.cn
 */publicclassHtmlUnitSpider{publicstaticvoid main(String[] s)throwsException{//目标网页String url ="http://www.yshjava.cn";//模拟特定浏览器FIREFOX_3WebClient spider =newWebClient(BrowserVersion.FIREFOX_3);//获取目标网页Page page = spider.getPage(url);//打印网页内容System.out.println(page.getWebResponse().getContentAsString());//关闭所有窗口
        spider.closeAllWindows();}}

 
Watij

Watij(发音wattage)是一个使用Java开发的Web应用程序测试工具,鉴于Watij的简单性和Java语言的强大能力,Watij能够使您在真正的浏览器中完成Web应用程序的自动化测试。因为是调用本地浏览器,因此支持CSS渲染和JS执行。

网页获取速度一般,IE版本过低(6/7)时可能会引发内存泄露。

范例代码如下:

package cn.ysh.studio.crawler.ie;import watij.runtime.ie.IE;/**
 * 基于Watij抓取网页内容,仅限Windows平台
 *
 * @author www.yshjava.cn
 */publicclassWatijTest{publicstaticvoid main(String[] s){//目标页面String url ="http://www.yshjava.cn";//实例化IE浏览器对象
        IE ie =new IE();try{//启动浏览器
            ie.start();//转到目标网页
            ie.goTo(url);//等待网页加载就绪
            ie.waitUntilReady();//打印页面内容System.out.println(ie.html());}catch(Exception e){
            e.printStackTrace();}finally{try{//关闭IE浏览器
                ie.close();}catch(Exception e){}}}}

 

Selenium
Selenium 也是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE、Mozilla Firefox、Mozilla Suite等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创 建衰退测试检验软件功能和用户需求。支持自动录制动作和自动生成。Net、Java、Perl等不同语言的测试脚本。Selenium 是ThoughtWorks专门为Web应用程序编写的一个验收测试工具。

网页获取速度较慢,对于爬虫来说,不是一个好的选择。

范例代码如下:

package cn.ysh.studio.crawler.selenium;import org.openqa.selenium.htmlunit.HtmlUnitDriver;/**
 * 基于HtmlDriver抓取网页内容
 *
 * @author www.yshjava.cn
 */publicclassHtmlDriverTest{publicstaticvoid main(String[] s){//目标网页String url ="http://www.yshjava.cn";HtmlUnitDriver driver =newHtmlUnitDriver();try{//禁用JS脚本功能
            driver.setJavascriptEnabled(false);//打开目标网页
            driver.get(url);//获取当前网页源码String html = driver.getPageSource();//打印网页源码System.out.println(html);}catch(Exception e){//打印堆栈信息
            e.printStackTrace();}finally{try{//关闭并退出
                driver.close();
                driver.quit();}catch(Exception e){}}}}

 

Webspec

一个开源的带有界面的Java浏览器,支持脚本执行和CSS渲染。速度一般。
范例代码如下:

package cn.ysh.studio.crawler.webspec;import org.watij.webspec.dsl.WebSpec;/**
 * 基于WebSpec抓取网页内容
 *
 * @author www.yshjava.cn
 */publicclassWebspecTest{publicstaticvoid main(String[] s){//目标网页String url ="http://www.yshjava.cn";//实例化浏览器对象WebSpec spec =newWebSpec().mozilla();//隐藏浏览器窗体
        spec.hide();//打开目标页面
        spec.open(url);//打印网页源码System.out.println(spec.source());//关闭所有窗口
        spec.closeAll();}}

源码下载:网络爬虫(网络蜘蛛)之网页抓取范例源码

转载源地址:http://www.cnblogs.com/chenying99/articles/3213533.html

[转载]微信支付通知的处理方式简要解析 - x3d - 博客园

mikel阅读(1097)

[转载]微信支付通知的处理方式简要解析 – x3d – 博客园.

通知机制的实现,官方只有文档没有demo代码,对没搞过的人来说,需要花大量时间来做测试。

 

从文档上说的来看,微信每次通知过来的数据,结构比较复杂,是一个多段数据,除了要取出POST数据外,还要取其它的数据。

 

这里首先涉及到一个关于php://input与$_POST取值的问题,简单列几点如下:

 

1,Content- Type取值为application/x-www-form-urlencoded时,php会将http请求body相应数据会填入到数 组$_POST,填入到$_POST数组中的数据是进行urldecode()解析的结果。(其实,除了该Content-Type,还有 multipart/form-data表示数据是表单数据,稍后我们介绍)
2,php://input数据,只要Content-Type不为 multipart/form-data(该条件限制稍后会介绍)。那么php://input数据与http entity body部分数据是一致的。该部分相一致的数据的长度由Content-Length指定。
3,仅当Content-Type为application/x-www-form-urlencoded且提交方法是POST方法时,$_POST数据与php://input数据才是”一致”(打上引号,表示它们格式不一致,内容一致)的。其它情况,它们都不一致。
4,php://input读取不到$_GET数据。是因为$_GET数据作为query_path写在http请求头部(header)的PATH字段,而不是写在http请求的body部分。

这也帮助我们理解了,为什么xml_rpc服务端读取数据都是通过file_get_contents(‘php://input’, ‘r’)。而不是从$_POST中读取,正是因为xml_rpc数据规格是xml,它的Content-Type是text/xml。
5. php://input碰到了multipart/form-data,请 查阅RFC1867对它的描述。multipart/form-data也表示以POST方法提交表单数据,它还伴随了文件上传,所以会跟 application/x- www-form-urlencoded数据格式不一样。它会以一更种更合理的,更高效的数据格式传递给服务端。当 Content-Type为multipart/form-data的时候,即便http请求body中存在数据,php://input也为空,PHP 此时,不会把数据填入php://input流。所以,可以确定: php://input不能用于读取enctype=multipart/form-data数据。

6. 当Content-Type为application/x- www-form-urlencoded时,php://input和$_POST数据是“一致”的,为其它Content-Type的时候,php: //input和$_POST数据数据是不一致的。因 为只有在Content-Type为application/x-www-form- urlencoded或者为multipart/form-data的时候,PHP才会将http请求数据包中的body相应部分数据填入$_POST全 局变量中,其它情况PHP都忽略。而php://input除了在数据类型为multipart/form-data之外为空外,其它情况都可能不为空

以上转述这么多文字的意思,就是说,得用到这两种方式来读取微信传过来的数据。

先取$POST 这是常规的支付通知信息,形如:

array (
  'bank_type' => '3006',
  'discount' => '0',
  'fee_type' => '1',
  'input_charset' => 'UTF-8',
  'notify_id' => 'YaNO6cznoNZK0aGb8nJWGgVUWssjt7Ze7gWRaRS0R_5w9oXgGNkRGxReEk0r45yk3I9a2_gzo9IqgqMYbap6bxC2T3p0o-2C',
  'out_trade_no' => '1214284731',
  'partner' => '12xxxxxxxx',
  'product_fee' => '3400',
  'sign' => '545FA0E8B594BBXXXX48XX142F084TY',
  'sign_type' => 'MD5',
  'time_end' => '20130223110224',
  'total_fee' => '3400',
  'trade_mode' => '1',
  'trade_state' => '0',
  'transaction_id' => '12XXX449012014XXX33174005XXX',
  'transport_fee' => '0',
)

再用file_get_contents(‘php://input’)读取额外的信息,形如:

<xml><OpenId><![CDATA[o0pd3jqHaN7b0tVPDFJPzJEkSCLw]]></OpenId>
<AppId><![CDATA[wxXXX06XX2cXXX88XX]]></AppId>
<IsSubscribe>1</IsSubscribe>
<TimeStamp>1400814743</TimeStamp>
<NonceStr><![CDATA[lqxwMsiY9EXRDpms]]></NonceStr>
<AppSignature><![CDATA[c2dxxxe186116b32b06axxxc1a688b671eexxx5e]]></AppSignature>
<SignMethod><![CDATA[sha1]]></SignMethod>
</xml>

最后,做相应的业务逻辑处理,就不详述了。

本文来源:http://www.cnblogs.com/x3d/,转载请注明。

[转载]炉石传说 C# 开发笔记 (续) - magicDict - 博客园

mikel阅读(1110)

[转载]炉石传说 C# 开发笔记 (续) – magicDict – 博客园.

炉石传说山寨的工作一直在进行着,在开发过程中深深体会到,对于业务的理解和整个程序的架构的整理远比开发难得多。

在开发过程中,如果你的模型不合理,不准确,很有可能造成代码的混乱,冗余,难以维护和扩展性比较差等问题。

当然,除去领域专家之外,很少人对于一个新的事物可以在一开始就把握住整个核心业务。

 

接下来讲讲整个程序的构造:

Card类库:将整个业务逻辑封装在里面,包括了服务器和客户端的通信逻辑。通信协议的编码和解码。现在是为了炉石定制的,以后想改写成更加通用的。

CardHelper:一个辅助程序,例如一些简单的单元测试,从Excel读取卡牌信息保存为XML等等

火炉服务器:一个简单的服务器

炉边传说:一个简单的客户端

 

程序最难的部分是理清楚一个客户端和服务器的通信流程,以及,各个模块的职责。

1.(本方客户端)本方使用一张手牌

2.(本方客户端)GameManager分析这种手牌的作用

3.(本方客户端)如果需要 抉择或者需要指定施法对象,则返回UI获得需要信息(UI和GameManager之间,使用delegate通信)

4.(本方客户端)使用法术的时候,将法术分解为效果,将法术名称记录到使用法术的日志(ActionList)

5.(本方客户端)法术模块进行施法动作的逻辑计算,修改本方的对象实例,将法术效果记录到使用法术的日志(ActionList)

6.(本方客户端)将法术的日志传送给服务器

7.(对方客户端)获得法术的日志

8,(对方客户端)通过法术名称,告诉用户被使用了法术

9.(对方客户端)根据法术效果修改本方的对象实例(这里没有逻辑计算,单纯的根据日志修改对象)

 

从整个游戏流程上说,大概是这个样子的

整个炉石的核心部分是法术效果

法术的卡牌,随从的战吼,亡语等等都可以看做为法术效果。

对于法术的分解,分解为最小单元则是最重要的事情。

例如:奥术飞弹 随机对目标发动3次攻击,每次1点伤害

这个法术在分解为原子法术的时候变为  3个原子法术效果

随机对目标发动1次攻击,每次1点伤害

每个原子法术的效果实施之后,都必须进行整个战场的再计算。

下面是整理的法术表格

希望有人帮助我整理资料,一个人力不从心了

 

 

不知道有人愿意为我开发客户端吗?服务器和核心类库的开发,我来完成,希望有一个对于客户端和美工比较在行的朋友,开发一个客户端。

C#的代码,可以考虑以后移植到Surface上去,或者通过Mono移植到Liunx去。

考虑到版权问题,可以做成三国主题的卡牌游戏

源代码:https://github.com/magicdict/MagicMongoDBTool

Card/Card Helper/火炉服务器/炉边传说   4个目录就可以了,其他的是MongoDB的项目源代码

考虑到以后用MongoDB做日志维护,暂时先放在一起管理了。

[转载]Android开发学习---android下的数据持久化,保存数据到rom文件,android_data目录下文件访问的权限控制 - Hi_Amos - 博客园

mikel阅读(1135)

[转载]Android开发学习—android下的数据持久化,保存数据到rom文件,android_data目录下文件访问的权限控制 – Hi_Amos – 博客园.

一.需求

做一个类似QQ登录似的app,将数据写到ROM文件里,并对数据进行回显.

二.截图

登录界面:

 

文件浏览器,查看文件的保存路径:/data/data/com.amos.datasave/files/LoginTest.txt——/data/data/(包名)/files/(文件名)

导出的文件内容:

 

 

三.实现代码

新建一个Android 工程.这里我选择的是2.1即API 7,进行开发的,其它都是默认下一步下一步即可.

/datasave/res/layout/activity_main.xml


&nbsp;
<button></button>

&nbsp;

&nbsp;

这里需要介绍的是Android:hint属性,作用是输入框里的显示的提示信息.相当于web项目中的placeholder.

/datasave/res/values/strings.xml

<!--?xml version="1.0" encoding="utf-8"?-->

手机qq登录save
Settings
请输入账号和密码:
请输入账号
请输入密码
登 录
记住密码

/datasave/src/com/amos/datasave/MainActivity.java

package com.amos.datasave;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {

String tag = "MainActivity";
EditText et_name;//用户名
EditText et_password;//密码
Button bt_login;//登录按钮
CheckBox cb_password;//单选框

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
et_name = (EditText) this.findViewById(R.id.et_name);
et_password = (EditText) this.findViewById(R.id.et_password);
bt_login = (Button) this.findViewById(R.id.bt_login);
cb_password = (CheckBox) this.findViewById(R.id.cb_password);

//注册点击事件
bt_login.setOnClickListener(this);

}

// 点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_login:

if (cb_password.isChecked()) {//如果单选框被选中了
//保存数据到rom
new savePasswordService(this).savePasswordToFile(et_name.getText().toString(), et_password.getText().toString());
Toast.makeText(this, "保存数据成功!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "没有保存数据!", Toast.LENGTH_SHORT).show();
}
}
}
}

注:这里主要注意的是进行判断时使用switch语句能更使代码结构更清晰.

/datasave/src/com/amos/datasave/savePasswordService.java

package com.amos.datasave;

import java.io.FileOutputStream;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;

@SuppressLint("WorldWriteableFiles")
public class savePasswordService {
private Context context;

private String tag = "savePasswordService";

public savePasswordService(Context context) {
this.context = context;
}

public void savePasswordToFile(String name, String password) {
// 这里设置文件的权限
String content = name + ":" + password;
Log.d(tag, "设置文件的读写权限");
try {
FileOutputStream fileOutput = context.openFileOutput("LoginTestConfig.txt", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
fileOutput.write(content.getBytes());
fileOutput.flush();
fileOutput.close();
} catch (Exception e) {
Log.d(tag, "设置文件的读写权限失败!");
e.printStackTrace();
}

}

}

注:这里是将用户名密码写到rom中的关键所在,主要调用了Context中的openFileOutput()方法,默认的权限是private,即其他应用程序不可读,这里我设置了其它应用程序可读写.

这里新建一个工程叫dataread,其界面设计不变,这里读取到LoginTestConfig.txt,然后往里面写数据,实验证明是OK的,写入成功.

package com.amos.dataread;

import java.io.File;
import java.io.FileOutputStream;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;

public class MainActivity extends Activity {
private String tag = "MainActivity";
EditText et_name;
EditText et_password;
Button bt_login;
CheckBox cb_remember;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(tag, "准备读取数据");
File file = new File("/data/data/com.amos.datasave/files/LoginTestConfig.txt");
try {
FileOutputStream fos =new FileOutputStream(file);
fos.write("112233:pwd".getBytes());
fos.close();

} catch (Exception e) {
e.printStackTrace();
}

}

}

如果想要实现数据回显,那么可以参考以下代码:

package com.amos.dataread;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
private String tag = "MainActivity";
EditText et_name;
EditText et_password;
Button bt_login;
CheckBox cb_remember;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) this.findViewById(R.id.et_name);
et_password = (EditText) this.findViewById(R.id.et_password);

Log.d(tag, "准备读取数据");
File file = new File("/data/data/com.amos.datasave/files/LoginTestConfig.txt");
try {
FileInputStream fis= new FileInputStream(file);
byte[] bytes = new byte[1024];

ByteArrayOutputStream bos = new ByteArrayOutputStream();
int len;
while((len=fis.read(bytes))&gt;0){
bos.write(bytes, 0, len);
}
String result = new String(bos.toByteArray());
Log.d(tag, result);
Toast.makeText(this, "读取数据成功!"+result, Toast.LENGTH_LONG);
String name = result.split(":")[0];
String password = result.split(":")[1];
Log.d(tag,name);
Log.d(tag,password);
et_name.setText(name);
et_password.setText(password);
} catch (Exception e) {
e.printStackTrace();
}

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

效果:

这个是写数据.

这个是读数据,即将数据回显.

 

这里一定要注意初始化EditText 输入框,否则会一直报空指针的错误.如下所示:

05-21 16:36:23.127: D/MainActivity(31834): 123456:password
05-21 16:36:23.173: W/System.err(31834): java.lang.NullPointerException
05-21 16:36:23.183: W/System.err(31834): at com.amos.dataread.MainActivity.onCreate(MainActivity.java:43)
05-21 16:36:23.193: W/System.err(31834): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
05-21 16:36:23.193: W/System.err(31834): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
05-21 16:36:23.193: W/System.err(31834): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
05-21 16:36:23.193: W/System.err(31834): at android.app.ActivityThread.access$2200(ActivityThread.java:119)
05-21 16:36:23.193: W/System.err(31834): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
05-21 16:36:23.193: W/System.err(31834): at android.os.Handler.dispatchMessage(Handler.java:99)
05-21 16:36:23.203: W/System.err(31834): at android.os.Looper.loop(Looper.java:123)
05-21 16:36:23.203: W/System.err(31834): at android.app.ActivityThread.main(ActivityThread.java:4363)
05-21 16:36:23.213: W/System.err(31834): at java.lang.reflect.Method.invokeNative(Native Method)
05-21 16:36:23.213: W/System.err(31834): at java.lang.reflect.Method.invoke(Method.java:521)
05-21 16:36:23.213: W/System.err(31834): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
05-21 16:36:23.223: W/System.err(31834): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
05-21 16:36:23.223: W/System.err(31834): at dalvik.system.NativeStart.main(Native Method)

时要注意的是还有一个权限,是Context.MODE_APPEND

MODE_PRIVATE = 0x0000;

MODE_WORLD_READABLE = 0x0001;

MODE_WORLD_WRITEABLE = 0x0002;

MODE_APPEND = 0x8000;

定义APPEND时,数据会追加到文件中,如果只是定义Context.MODE_APPEND,那么其它应用程序是不能访问此文件的,默认是私有的.

四.总结

1.eclipse中的logcat没有输出任何内容

Window–>Prefrences–>Android–>LogCat—>Switch to Java, priority at least VERBOSE

 

2.File explorer 中看不到内容

Window->show view -> other -> Android -> device 

看一下有没有你的模拟器,如果有,那么再点击File explorer,这样就能看到文件了.

再点击pull.push 就能将文件传出.传入了.右上角的两个小图标.

[转载]或许您还不知道的八款Android开源游戏引擎-游戏开发分享与问答-eoe 移动开发者论坛 - Powered by Discuz!

mikel阅读(1099)

[转载]或许您还不知道的八款Android开源游戏引擎-游戏开发分享与问答-eoe 移动开发者论坛 – Powered by Discuz!.很多初学android游戏开发的 朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引擎可供使用,一边自暴自弃的抱怨Android平台游戏开发难度太高,又连个像样的游 戏引擎也没有,甚至误以为使用Java语言开发游戏是一件费力不讨好且没有出路的事情。

事实上,这种想法完全是没有必要且不符合实际的,作为能和苹果iOS分庭抗礼的Android(各种意义上),当然也会有相当数量的游戏引擎存在。仅仅因为我们处于这个狭小的天地间,与外界接触不够,所以对它们的存在茫然不知罢了。

下面我就罗列出八款常见的Android游戏引擎,以供有需要者参考(收费,下载量过小,不公布源码,以及鄙人不知道(-_-)的引擎不在此列)。

1、Angle

Angle是一款专为Android平台设计的,敏捷且适合快速开发的2D游戏引擎,基于OpenGL ES技术开发。该引擎全部用Java代码编写,并且可以根据自己的需要替换里面的实现,缺陷在于文档不足,而且下载的代码中仅仅包含有少量的示例教程。

最低运行环境要求不详。

项目地址:http://code.google.com/p/angle/

2、Rokon

rokon是一款Android 2D游戏引擎,基于OpenGL ES技术开发,物理引擎为Box2D,因此能够实现一些较为复杂的物理效果,该项目最新版本为 2.0.3 (09/07/10)。总体来说,此引擎最大的优点在于其开发文档相当之完备,并且项目作者对反馈Bug的修正非常之神速,所以该框架的使用在目前也最为 广泛,有人干脆将它称为Cocos2d-iPhone引擎的Android版(业务逻辑和编码风格上也确实很像)。附带一提,国内某个需要注册会员才能下 载的Android游戏框架衍生于此框架,所以大家也不要刻板的认为收费便一定是好的,免费就一定不好。

最低运行环境要求为Android 1.5。

项目地址:http://code.google.com/p/rokon/

3、LGame

LGame是一款国人开发的Java游戏引擎,有Android及PC(J2SE)两个开发版本,目前最高版本同为0.2.6(31/07/10)。其底 层绘图器LGrpaphics封装有J2SE以及J2ME提供的全部Graphics API(PC版采用Graphics2D封装,Android版采用Canvas模拟实现),所以能够将J2SE或J2ME开发经验直接套用其中,两版本 间主要代码能够相互移植。Android版内置有Admob接口,可以不必配置XML直接硬编码Admob广告信息。

该引擎除了基本的音效、图形、物理、精灵等常用组件以外,也内置有Ioc、xml、http等常用Java组件的封装,代价是jar体积较为庞大,PC版 已突破1.2MB,Android版有所简化也在500KB左右。此外,该引擎还内置有按照1:1实现的J2ME精灵类及相关组件,可以将绝大多数 J2ME游戏平移到Android或PC版中。唯一遗憾的是,该项目作者是个极其懒惰的家伙,开发文档从去年说到今年依旧没有提供,只有游戏示例可供下 载。

最低运行环境要求为Android 1.1。

项目地址:http://code.google.com/p/loon-simple/

4、AndEngine

andengine同样是一款基于OpenGL ES技术的Android游戏引擎,物理引擎同样为Box2D(标配|||)。该框架性能普通,文档缺乏,但示例较为丰富。

最低运行环境要求不详。

下载地址(未直接提供jar下载,源码可通过svn提取):http://code.google.com/p/andengine/

5、libgdx

libgdx是一款基于OpenGL ES技术开发的Android游戏引擎,支持Android平台下的2D游戏开发,物理引擎采用Box2D实现。单就性能角度来说,堪称是一款非常强大的 Android游戏引擎,但缺陷在于精灵类等相关组件在使用上不够简化,而且文档也较为匮乏。

最低运行环境要求不详。

项目地址:http://code.google.com/p/libgdx/

6、jPCT

jPCT是一款基于OpenGL技术开发的3D图形引擎(PC环境为标准OpenGL,Android为OpenGL ES), 以Java语言为基础的,拥有功能强大的Java 3D解决方案。该引擎与LGame(此为2D游戏引擎)相类似,目前拥有PC(J2SE)以及Android两个开发版本。

jPCT的最大优势之一,就在于它惊人的向下兼容性。在PC环境中,jPCT甚至可以运行在JVM1.1环境之中,因为jPCT内部提供的图形渲染接口完 全符合所有的Java 1.1规范(就连已经消失的Microsoft VM乃至更古老的Netscape 4 VM也不例外)。

最低运行环境要求为Android 1.5。

项目地址:http://www.jpct.net/jpct-ae/

7、Alien3d

Alien3d是一款体积非常之小的Android 3D游戏引擎,基于OpenGL ES技术开发。为了压缩体积,它根据不同功能采用多jar方式发布(包括alien3d-engine.jar,alien3d- tiled.jar,alien3d-sprites.jar,alien3d-shapes.jar,alien3d- particles2d.jar,),事实上它的核心文件大约只有40KB,所有相关jar的总和也不足150KB。

最低运行环境要求为Android 1.5。

项目地址:http://code.google.com/p/alien3d/

8、Catcake

Catcake是一款跨平台的Java 3D图形引擎,目前支持PC(J2SE)及Android环境运行(已有iPhone版规划)。该引擎在易用性和运行性能上皆有出色的表现,支持常见的游戏开发功能,诸如精灵动画,音频处理和视频播放等。

最低运行环境要求为Android 1.6。

项目地址:http://code.google.com/p/catcake/

[转载]使用Source Safe for SQL Server解决数据库版本管理问题 - CareySon - 博客园

mikel阅读(1113)

[转载]使用Source Safe for SQL Server解决数据库版本管理问题 – CareySon – 博客园.

简介

在软件开发过程中,版本控制是一个广为人知的概念。因为一个项目可能会需要不同角色人员的参与,通过使用版本控制软件,可以使得项目中不同角色的人并行参 与到项目当中。源代码控制使得代码可以存在多个版本,而不会将代码库变得混乱,典型的场景包括Bug修复、添加新功能、版本整合等。

虽然在开发层面的版本控制软件已经非常成熟,但目前国内还没有专门针对数据库层面的版本控制软件来帮助不同角色的人员在数据库层面进行团队协作、变更代码管理以及对数据库的变更进行查看和比对。在数据库层面版本控制工具的缺乏可能会出现如下场景:

  • 无法在数据库层面进行团队协作:开发人员A对存储过程的修改导致开发人员B创建的存储过程被覆盖,从而无法比对和追踪
  • 开发人员-开发DBA-测试人员难以协作:SVN等版本工具是基于文件的,很难在数据库层面进行版本控制
  • 数据库发生的变更难以追踪:现有的技术无法追踪由谁、在什么时间、对数据库修改了什么,当发生由数据库引起的报错或性能下降时,难以排查
  • 无法记录对数据库变更的过程资产:数据库变更的历史记录只有数据库运维人员了解,当该相关人员离职或调岗,这些过程资产难以继承
  • 难以审计数据库:现有的数据库审计功能往往依赖于日志,对性能造成很大影响。
  • SQL脚本无法有效管理:现有的做法往往是将SQL以文件形式保存,无法有效管理和共享
  • 无法查看被加密的数据库对象:当需要对加密的数据库对象进行修改时,如果无法找到对象定义的原始记录,则必须重写该对象
  • SVN建立和使用复杂:SVN使用流程对于数据库人员过于繁琐,为数据库人员增加了额外的工作负担

 

由于数据库是整个业务应用的核心,上述问题无论是在开发环境还是在生产环境如果得不到有效的解决,会造成生产力低下、过程资产无法得到管理、数据库审查无法进行、难以排查由数据库变更导致的问题等情况。

 

下面来介绍一下Source Safe for SQL Server如何解决该类问题。

 

软件的安装

软件的官网下载完Source Safe的安装包后一路下一步,安装完成后打开Management Studio,在需要加入到源代码控制器的某个数据库服务器上右键,在弹出菜单中选择“添加数据库到版本控制”,如图1所示。

1

图1.将数据库添加到版本控制

 

然后设置相关的选项,如图2所示。

2

图2.添加数据库到版本控制相关设置

 

现在再来看,整个数据库都已经在版本控制之下了,如图3所示。

3

图3.查看受版本控制的数据库

 

至此,Source Safefor SQL Server就安装配置完成了。

 

典型应用场景

Source Safe可以解决下述问题:

 

开发团队进行版本控制

在一个开发团队中,对于数据库对象的每一次变更都会自动覆盖上一个版本,导致上一个版本对象定义的丢失。比如说开发人员A创建了存储过程“ProcA”, 然后开发人员B修改了存储过程“ProcA”,开发人员A创建的存储过程将会被开发人员B所做的修改覆盖,造成之前定义存储过程的丢失和无法回滚。使用 Source Safe可以轻松解决该类问题。

通过图4,我们看到存储过程“ProcA”由开发人员“Jack”创建后,开发人员“CareySon”对其进行了修改,Source Safe可以完整的记录由谁,在什么时间,做了哪些修改,如图所示。

1

图4.查看存储过程“ProcA”被修改的历史记录

 

2

图5.查看存储过程 “ProcA”两个版本的差异部分

 

开发里程碑标记

在开发过程中,往往需要对开发里程碑进行迭代,每一个开发里程碑导致的数据库对象变更都可以完整的被记录和文档化。

3

图6.里程碑版本以及涉及到的对象变更

 

生产环境变更管理

在生产环境中,程序的升级、程序或人为对数据库对象的更改有可能导致数据库出现问题,例如应用程序报错或数据库性能下降。通过SourceSafe可以快 速比较出数据库之前版本和当前数据库定义中存在差异的部分,并根据具体情况回滚导致数据库出现问题的对象,从而快速排除错误并保证数据库持续稳定运行。如 图7所示。

4

图7.选择需要版本比较的对象

 

常用脚本管理

无论是开发人员还是数据库的运维人员,都会有常用脚本需要保存。过去的做法往往是将SQL代码以文件的形式保存,这样既不方便使用,也不方便分类管理。利 用Source Safe的代码管理功能,可以方便的将SQL代码的管理无缝集成到SQL Server Management Studio中。如图8所示。

5

图8、使用Source Safe的脚本管理功能对SQL进行管理

 

此外,Source Safe特别设定了默认文件夹“工具栏快捷方式”,用户可以将频繁使用的SQL代码置于此处,在该分类下的脚本会自动出现在Management Studio的工具栏中,如图9所示。

6

图9、将常用脚本置于“工具栏快捷方式”中

 

对选定的对象进行版本归类

虽然Source Safe每次同步之后都会生成一个基于变更的版本号,但在某些特殊情况下,比如需要对库中某些变更进行管理、对库中的版本进行归类的情况下会需要额外的标签。如图10所示。

7

图10、利用标签标出某个业务版本涉及到的数据库对象

 

 

与SVN的无缝集成

SourceSafe的版本内容和历史记录可以直接导出到SVN、TFS、VSS中,从而打通数据库版本控制和现有的SVN系统。在数据库中我们对存储过程dbo.TestProc做了三次变更,如图11所示。

010

图11.对存储过程的3次变更

 

接下来将历史记录导出到SVN,如图12所示。

011

图12.将SourceSafe记录导出到SVN

 

导出完成后,通过SVN客户端软件可以看到对应SQL文件的在数据库中对应图10的三次变更,如图13所示。

012

图13.SVN对应SourceSafe中的3次变更

 

软件的下载

软件的下载可以在软件的官网下载,下载地址请猛击这里(http://www.grqsh.com/products.htm?tab=sourcesafe-for-sql-server)