月度存档: 11月 2012

游戏开发准备(三)--传输协议

google 的 protobuf 是最近非常火的将结构对象进行序列化及反序列化的工具,据google称,已经应用在了google非常多的内部项目上。

1、下载protobuf,protoc:

https://code.google.com/p/protobuf/downloads/detail?name=protobuf-2.4.1.tar.bz2&can=2&q=

https://code.google.com/p/protobuf/downloads/detail?name=protoc-2.4.1-win32.zip&can=2&q=

2、下载下来的protoc放在 protobuf/java/src目录下

3、在protobuf/java目录下修改pom.xml(仅windows下需要修改)

<exec executable=”../src/protoc”>

修改为:

<exec executable=”src\protoc.exe”>

4、执行:

mvn test

mvn install 安装至 maven repository

mvn package 可以打包成jar包,供单独使用

mvn install -P lite 生成lite版本,lite缺少描述及反射功能,但在手机平台上推荐使用

这里我也打包了jar包,用来学习时使用。

5、在eclipse中创建java工程,来学习一下protobuf的使用

建立addressbook.proto

option java_package = “com.example.tutorial”;

message Person {
required string name = 1;
required int32 id = 2;        // Unique ID number for this person.
optional string email = 3;

enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}

message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}

repeated PhoneNumber phone = 4;
}

// Our address book file is just one of these.
message AddressBook {
repeated Person person = 1;
}

执行:

protoc addressbook –java-out=.

使用方式:

Person p = Person.newBuilder().setName(“hello world”).setId(111)
.build();
System.out.println(p.toByteArray().length);

toByteArray()进行序列化,parseFrom()进行反序列化。

下面在maven中集成:

添加plugin:

<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<tasks>
<mkdir dir=”target/generated-sources” />
<exec executable=”protoc”>
<arg
value=”–java_out=target/generated-sources” />
<arg
value=”src/main/protobuf/test.proto” />
</exec>
</tasks>
<sourceRoot>
target/generated-sources
</sourceRoot>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>

添加dependency:

<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.4.1</version>
</dependency>

里面有一个反射机制,需要查一下能起到什么作用

optimize_for 这个选项需要关注一下,如果开发手机网游,这个选项会有大用处。

option optimize_for = SPEED; // 默认

CODE_SIZE

LITE_RUNTIME //手机网游推荐使用这种

另外,需要检查一下protobuf的压缩。测试了一下,字符串好像并未压缩。

从smartfox上查找一下他们用的压缩工具,继续测试一下。

决定使用snappy算法。

https://github.com/dain/snappy

各种压缩算法在java平台上的测试结果。

实际测试后压缩效果不尽如人意,使用了deflate和snappy压缩算法,分别测试了小于20字节和120字节的压缩,只能压缩不到20%,初始怀疑protobuf的二进制已经充分优化过了,如果是这样的话,再消耗cpu进行压缩就没有必要了。但是也不排除测试的样本太少。可以等正式上线后,再回头进行压缩的测试。对了,snappy必须在jdk7下运行,因为用了到sun的非公开函数,所以需要注意一下是目标环境是否支持。

使用protobuf还有一个好处,可以避免使用java反射机制带来的效率降低。

游戏开发准备(二)--服务器端准备工作

新的游戏准备继续使用appfuse。

接触到的两款游戏没有放在web容器中执行,但我希望依旧保留web功能,在未来会有一些交互性及管理的功能使用web来实现。

而且mina也支持与tomcat整合。

OK。下面开始准备新的appfuse 2.0工程:

1、JDK 6+

2、mysql 5.5+(我使用的是mysql5.0,但我认为已经足够了,没有必要进行升级)

3、需要修改mail.properties,发送mail的功能随后我会修改掉,可以忽略这条。

4、Maven 3.0.4+,(另外还得准备好maven 2.x版本,full-source在3.x下无法执行通过)

下面开始生成项目文件:

1、mvn archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes -DarchetypeArtifactId=appfuse-basic-struts-archetype -DarchetypeVersion=2.1.0 -DgroupId=org.haifi -DartifactId=igame -DarchetypeRepository=http://oss.sonatype.org/content/repositories/appfuse

2、进入igame目录,修改pom.xml,设置db连接,执行:mvn jetty:run-war

可以在浏览器访问:http://127.0.0.1:8080/

3、执行:mvn appfuse:full-source (这里需要maven 2.x来执行)

如果出现错误:

[FATAL] Non-resolvable parent POM: Could not find artifact org.appfuse:appfuse:p
om:2.2-SNAPSHOT and ‘parent.relativePath’ points at wrong local POM @ line 21, c
olumn 13

那就表示你用了maven 3.x。

添加包依赖,不知道为什么默认没有。缺少包可以去http://search.maven.org下载

<aspectj.version>1.6.9</aspectj.version>
<commons.beanutils.version>1.8.3</commons.beanutils.version>
<commons.collections.version>3.2.1</commons.collections.version>
<commons.dbcp.version>1.3</commons.dbcp.version>
<commons.lang.version>2.5</commons.lang.version>
<compass.version>2.2.0</compass.version>
<cxf.version>2.2.4</cxf.version>
<dwr.version>2.0.1</dwr.version>
<ehcache.version>2.2.0</ehcache.version>
<ehcache.web.version>2.0.2</ehcache.web.version>
<hibernate.annotations.version>3.6.0-SNAPSHOT</hibernate.annotations.version>
<hibernate.version>3.6.9.Final</hibernate.version>
<jpa.version>2.0.Beta2</jpa.version>
<javamail.version>1.4.1</javamail.version>
<jstl.version>1.1.2</jstl.version>
<log4j.version>1.2.16</log4j.version>
<sitemesh.version>2.4.2</sitemesh.version>
<slf4j.version>1.6.1</slf4j.version>
<spring.security.version>3.0.4.RELEASE</spring.security.version>
<struts.menu.version>2.4.3</struts.menu.version>
<urlrewrite.version>3.1.0</urlrewrite.version>
<velocity.version>1.4</velocity.version>

另外添加:

<repository>
<id>central</id>
<url>http://search.maven.org</url>
</repository>
<repository>
<id>appfuse</id>
<url>https://repository.sonatype.org/content/groups/public</url>
</repository>
<repository>
<id>springsource-repo</id>
<name>springsource repository</name>
<url>http://repo.springsource.org/release</url>
</repository>
<repository>
<id>repository.jboss.org-public</id>
<name>JBoss repository</name>
<url>https://repository.jboss.org/nexus/content/groups/public</url>
</repository>

4、下载源码包:mvn dependency:sources

5、mvn org.apache.maven.plugins:maven-eclipse-plugin:2.6:eclipse

然后在eclipse中导入工程。

继续后续修改:

1、pom.xml中修改

<amp.genericCore>false</amp.genericCore>
<amp.fullSource>true</amp.fullSource>

2、properties语种文件中我们只保留 zh_EN,zh_TW,en及默认,其它删除。

删除test目录下的jdbc.properties,log4j.xml

3、我把整个工程的字符集设置为UTF8

或者更好的一种方式:

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>

4、web.xml中启用lazyloadfilter,gzipfilter暂时和xfire有冲突,暂时不启用。

5、优化 applicationContext-resources.xml中设置

<bean id=”dataSource”
class=”org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy”>
<property name=”targetDataSource” ref=”targetDataSource” />
</bean>

可以避免空事务去连数据库

6、修改applicationContext-dao.xml

添加:

hibernate.hbm2ddl.auto=update
hibernate.show_sql=false

7、添加mina支持

<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>${mina.version}</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-integration-beans</artifactId>
<version>${mina.version}</version>
</dependency>

因为mian打包格式是bundle,需要添加:

<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
</plugin>

在spring中添加

<!– mina game server –>
<bean id=”trapHandler”
init-method=”start” />

<bean id=”snmpCodecFilter”
class=”org.apache.mina.filter.codec.ProtocolCodecFilter”>
<constructor-arg>
<bean
class=”org.apache.mina.filter.codec.textline.TextLineCodecFactory” />
</constructor-arg>
</bean>

<bean id=”loggingFilter”
class=”org.apache.mina.filter.logging.LoggingFilter” />

<!– The filter chain. –>
<bean id=”filterChainBuilder”
class=”org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder”>
<property name=”filters”>
<map>
<entry key=”loggingFilter” value-ref=”loggingFilter” />
<entry key=”codecFilter” value-ref=”snmpCodecFilter” />
</map>
</property>
</bean>
<bean
class=”org.springframework.beans.factory.config.CustomEditorConfigurer”>
<property name=”customEditors”>
<map>
<entry key=”java.net.SocketAddress”>
<bean
class=”org.apache.mina.integration.beans.InetSocketAddressEditor” />
</entry>
</map>
</property>
</bean>
<bean id=”ioAcceptor”
class=”org.apache.mina.transport.socket.nio.NioSocketAcceptor”
init-method=”bind” destroy-method=”unbind” lazy-init=”false”>
<property name=”defaultLocalAddress” value=”:8888″ />
<property name=”handler” ref=”trapHandler” />
<property name=”filterChainBuilder” ref=”filterChainBuilder” />
</bean>

8、创建测试样例类:

在src/test/java下建立

public class GameBaseManagerTestCase extends BaseManagerTestCase {

// 这里指定testcase读取的spring配置文件

protected String[] getConfigLocations() {
setAutowireMode(AUTOWIRE_BY_NAME);

return new String[] { “/applicationContext-resources.xml”,
“classpath:/applicationContext-dao.xml”,
“/applicationContext-service.xml”,
“classpath*:/**/applicationContext-event.xml”,
“classpath*:/**/applicationContext-ds.xml”,
“classpath*:/**/applicationContext.xml”,
“classpath*:/**/applicationContext-activemq.xml”,
“classpath*:/**/applicationContext-job.xml”,
“classpath*:/**/remoting-servlet.xml”, };
}

// 这里添加自己多增加的资源文件,可以让测试样例读取到
@Override
protected void onSetUp() throws Exception {
super.onSetUp();
//
// 使用LocalizedTextUtil可以读取到wgame中的资源文件
LocalizedTextUtil.addDefaultResourceBundle(“ApplicationResources”);
LocalizedTextUtil.addDefaultResourceBundle(“ApplicationResources_enum”);
LocalizedTextUtil.addDefaultResourceBundle(“ApplicationResources_log”);
LocalizedTextUtil
.addDefaultResourceBundle(“ApplicationResources_exception”);
// 初始化测试对象
if (initPlayers) {
initPlayers();
}
time = System.currentTimeMillis();
GameDynamicElement.skipTournament=true;
log.debug(“———————- start ” + getName()
+ ” ———————–“);

}

 

9、因为工程转为使用UTF-8编码,如果遇到如下错误“ 编码 GB18030 的不可映射字符”,在pom.xml中修改如下:

<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>

10:为maven-javadoc添加额外的java源码目录:

<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/protobuf/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>

或者用另一种方式:

<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
<configuration>
<excludePackageNames>
org.haifi.webapp:org.haifi.dao:org.haifi.service:org.haifi.game.reflection:org.haifi.game.channel:org.haifi.game.compiler:org.haifi.game.mina
</excludePackageNames>
<sourcepath>${basedir}/src/main/java;${basedir}/src/main/protobuf/java</sourcepath>
</configuration>

直接指定源文件所在目录

11:添加spring http invoker支持:

定义applicationContext-remoting.xml文件:

<bean name=”/GameUserService”
class=”org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter”>
<property name=”service” ref=”localGameUserRemotingManager” />
<property name=”serviceInterface”
value=”org.haifi.game.service.GameUserRemotingManager” />
</bean>

<!– 定义为local,可以提供给测试样例调用 –>
<bean name=”localGameUserRemotingManager”
class=”org.haifi.game.service.impl.GameUserRemotingManagerImpl” />

web.xml中添加:

<!– 支持sprint http invoker –>
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>namespace</param-name>
<param-value>applicationContext-remoting</param-value>
</init-param>
<!– default context config location:remoting-servlet.xml –>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/gr/*</url-pattern>
</servlet-mapping>

可是浪费了好几个小时怎么还不生效呢。原来是urlwrite搞的鬼:

urlrewrite中添加:

<rule>
<from>/gr/**</from>
<to>/gr/$1</to>
</rule>

好了,下面开始客户端进行调用:因为游戏中的url是不固定的,所以需要用代码生成client:

HttpInvokerProxyFactoryBean bean = new HttpInvokerProxyFactoryBean();
bean.setServiceUrl(“http://127.0.0.1:8080/gr/GameUserService”);
bean.setServiceInterface(GameUserRemotingManager.class);
bean.setHttpInvokerRequestExecutor(new CommonsHttpInvokerRequestExecutor());
bean.afterPropertiesSet();
GameUserRemotingManager obj = (GameUserRemotingManager) bean
.getObject();
log.info(obj);

游戏开发准备(-)--配置应该在哪里 

配置是指游戏中所必须的初始数据,包含npc,装备,对话等所有相关内容。

配置应该存放在excel中,策划人员更愿意去修改excel。

存放在excel中的配置,应该保证只修改,增加,不要删除,因为id可能已经被玩家数据引用到了。

为了方便程序员容易处理excel至游戏数据的转化,我们可以指定第一行为属性名,使用java的反射机制直接生成java对象,这个工具还需要手动来实现,过程不是很麻烦。

如果为了更高的要求,可以在excel中支持公式计算的结果(这种结果对策划来说交互性更好)。