RIX 编程应该是快乐的

26八/100

一起做网游吧【9.5】:补丁,设置服务器地址和端口及网页管理界面更新

首先,代码还在上次的位置,我替换了压缩文件,不用再说,以后也是如此。

前几天,我准备找一个free的地图编辑器,当然,最好是open source的,同时,看看dojo,研究dojo而不是extjs是由于相比来说,dojo的模块划分我比较喜欢,而且,dojo中的测试程序都很有针对性和独立性,这个我比较喜欢。顺便将服务器的管理界面更新了下,修改成了基于dojo的。我对于extjs并没有什么恨意,只是由于不太习惯风格而已,对于extjs比较熟悉的人可能觉得extjs比较好,不管怎么,这只是一个风格而已。顺便提下学习的过程,半天时间来熟悉下小例子,半天时间将界面的布局弄好,一天时间将表格加上,用了各种办法,大概花了两天时间研究出了如何更新表格内容(可能不是正规的方法,谁要是知道的话,还请赐教)。

前段时间,我也又在落灰的wii上安装了erlang,然后将服务器仍在那个上面,运行,然后客户端在切换服务器的时候客户端挂掉!!!,原因在于服务器返回的地址是"127.0.0.1",这是个比较严重的问题,决定将进入游戏之后的内容稍微缓和一下,反正地图编辑器还没有怎么熟悉,地图的资源也要慢慢找,就顺便的添加了下服务器的IP和端口设定,使服务器启动的时候还是监听本地的"127.0.0.1",返还给客户端的可以是自定义的IP地址,提供端口设定是考虑到可能需要路由进行端口映射。

先说服务器内容,使用下面的函数可以获得服务器的公网IP,至于能不能公网直接访问,这就要看你的服务器的IP设定了。

?Download server.erl
1
2
3
4
5
6
7
8
9
10
       getip() ->
    case http:request(get, {"http://www.ip138.com/ip2city.asp",[]},[],[{version, 1.1}]) of 
        {ok, {{_,Ok,_},_, Content}} ->
            Index1 = string:rstr(Content, "["),
            Index2 = string:rstr(Content, "]"),
            Ip = string:sub_string(Content, Index1+1, Index2-1),
            Ip;
        Other ->
            io:format("~p~n", [Other])
    end.

我是从ip138.com获得的公网IP,希望不会带来什么怨言。同时,添加了一个reload函数,因为现在服务器还不是非常的问题,可能由于某些未处理的原因,造成服务器的端口监听关闭,使用下面这个函数可以重新加载。需要传递IP和Port的话,则是根据设定的ip2和port2来判定的:

16八/100

一起做网游吧【9】:多服务器支持

兑现上周的承诺

代码的位置:

;

先说下编译的方法,满足某些人的好奇心。截图就不提了,之前的有。

由于这次引入了web的方式,所以编译的准备工作有些麻烦。我使用mochiweb来作为web的服务器端程序,extjs作为客户端的javascript程序库,这两个库均未包含在git的仓库中,但可以通过执行others目录中的get_others.sh来获取,这个简单的脚本将获取mochiweb和extjs,并放置到相应的位置中,同时还设置了几个链接。之所以设置链接是由于压缩成7z之后,原来的链接特性消失了,维护不方便。对于ubuntu 10.04和fedora13来说,还有一个问题是关于mochiweb的,貌似最新的mochiweb需要使用erl R14编译,但这两个发行版都是R13(其他的我没有试),编译的时候会出错的,最简单的就是将每个erl最后的那些个测试删掉就可以了。如果服务器的源代码编译不过去,则可能是中文注释的问题,之前有erlang的中文注释补丁的办法,这里不说了。

首先执行get_others.sh,然后到server下执行make,到client下面最里面的makefile目录下执行make,就完成了。(当然,环境变量什么的还是需要手动设置的。)

运行就是以前的start-dev.sh,没什么好说的。下面开始技术分析:

在记录中(schema.hrl)的client段,变成了下面的样子:

?Download schema.hrl
1
2
3
4
5
6
7
       -record(client, {server = none, 
                 player = none,
                 server_name = none,
                 server_port = none,
                 server_ip = none,
                 server_pid = none,
                 game=none}).

添加的变量类型主要是为了进行多服务器的支持,server_pid为port端口的pid值,这样客户端来区分当前在哪一个服务器上。其他的没什么说明的。

server数据库中的记录格式:

?Download schema.hrl
1
2
3
4
5
6
7
8
9
10
11
        -record(server, {
          id,
          socketpid = none,
          ip = none,
          port = none,
          name = none,
          enable = false,
          maxuser = 5000,
          curuser = 0,
          pid = none
          }).
15六/100

一起做网游吧【7】:服务器端注册和登录处理

这段时间老婆回家带孩子了,什么都要自己一个人弄了。最重要的就是找吃的和找穿的了,我这个人向来比较随遇而安的,比较好伺候,但这些无疑占用了些时间。她在我旁边么,这些东西都不用操心,不过要陪她玩什么的,她也不让我过多的碰电脑(为了我的眼睛),所以总的来说一半对一半。就像之前的那篇,关于数据库的创建。之前我本来准备那个不用动的,每个服务器不都是要一些配置么,当初觉得那么安排比较合理。但这段时间在准备下一步的过程中,觉得那样手动处理的比较多,容易出问题,以及在与朋友们的聊天中,也略有感受,还有一个原因就是我希望可以做一个定制,光盘放进去,启动电脑就可以了,原来的那些操作显然不适合。所以这里就几乎将之前的那个代码推翻了。想必以后这样的状况会越来越多,这个教程在我自己方面的初衷就是记录过程,而不是为了证明什么。所以出现反复是一定的了。

废话呢,就这些,这次的更改,主要是添加了注释,由于中文注释的关系,给erlang做了个补丁,编译的脚本也调了下:

26五/100

一起做网游吧【6】:创建数据库

之前提到了数据结构,现在把数据库的创建代码提供,没什么好说的,简单的查下资料,基本上什么都明白了。

5五/100

一起做网游吧【5】:服务器架构

生活还要继续,代码还是要不断的写。这次的代码是为未来写的,因为我现在在windows下,而基础的结构代码大部分都写了好,现在在整理,同时进行这个教程。整理好的代码都在linux下,在windows下却又没办法访问。虚拟机还在编译我定制的系统。这些都在之前提到过,因此,先将代码整理出来,回头切换系统的时候再添加进去,然后接着写新的。
这个系统的启动结构,没记错的话,应该是从mochiweb学来的,《Erlang程序设计》也提到过。首先定义一个app:

26四/100

一起做网游吧【4】:编译与执行脚本

在上一次说完之后,经过了不断的中断和不断的磨难,决定接着往前走。就像目前这篇文章,在经过不断的重启,被迫重写,最后才展现。虽然还不知道等下能不能发出去,但希望能发出去。
首先要说明的是目录结构等很多地方都参考了mochiweb,所以,如果看到类似的代码或者结构的话,不要感到奇怪。先是运行脚本和调试运行脚本,两个的区别在于调试运行可以检测到代码的更新,并强制更新(自我感觉是这样子的,因为我基本上没有运行过运行脚本)。

?Download start.sh
1
2
3
4
#!/bin/sh
#file server/start.sh
cd `dirname $0`
exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s netgame
?Download start-dev.sh
1
2
3
4
#!/bin/sh
#file server/start-dev.sh
cd `dirname $0`
exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s netgame -sname rix -heart -setcookie netgame

上面的代码,start.sh应该还需要添加-sname rix等参数,因为我基本上没用过,所以也就没有添加,随自己喜好吧。

5四/100

一起做网游吧【3】:数据库结构

趁着老婆还在睡觉,可以再更新一下网游教程的内容。本来这个内容要放到上一节中的,但写的时候忘了,只好单独做一个。
先看下数据的定义:

?View Code ERLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
       -record( counter, {
           type,
           value
          } ) .
-record( game, {
           id,
           enable = false,
           pid = none,
           name,
           info=none,
           test_mode = true
          }).
-record( player, {
           count_id,
           id,
           nick,
           password,
           login_errors = 0,
           pid = none,
           socket = none,
           disabled = false,
           online = false,
           ingame = false,
           now_play=0,
           game=[]
          } ).
-record(client, {server = none, player = none, game=none}).

其实,我对上述的数据定义的某些内容并不是绝对的非常的熟悉(之前提到过,数据库的内容我知道的很少)。因为在我刚开始学习erlang的时候,看到了Joe Armstrong提到的openpoker(我希望是这个名字,好几年前了)的服务器,当时在国外,费了好几天的时间,才找到最后公开的源代码,然后,就照搬了一下。比如对于上面的counter,就从未查看过它存的到底是什么内容。从使用的方法上来说,主要是记录其他数据表的个数,然后,作为每个数据在数据表中的唯一的id。这里的每个数据表都是set类型的,也就是说,一对一的。由于erlang的record的中,是否是同一个记录,看record的第一个元素,所以player就有了一个count_id的属性。count_id主要是防止id重合的,最后有没有用到需要到最后查看代码才能知道。
game,从实际上来讲,这个并非其本意,因为在真实的网游服务器中,这是一个节点或者一个进程,至于这个进程的功能,可能是一个大型的游戏,也可能是一个很微型的游戏(比如猜拳),甚至是某个特定功能的npc之类的,在这里,我基本上都是一个小游戏,因为这个教程并不想做的非常的复杂和非常的大,这样,重复的代码比较少。而我之前可能也提到过,这个游戏可以动态的添加或者减少某些小游戏。id对于某个小游戏来说,是唯一的,客户端参加哪一个小游戏,也主要是靠这个id。enable是表明该游戏是否可以允许客户接入,如果不允许的话,客户不应该看到这个游戏。name不用说了,就是游戏的名字。info,本来是存储各个小游戏自己的结构的,但最后没有用到,在这次的整理过程中,也说不定会用到,就暂时留着。test_mode,主要指定小游戏目前是否处于测试状态,在本篇教程中,刚开始的小游戏一直是test_mode,而如果一个游戏是test_mode的话,就需要手动的来启动,而不能随着主程序一起启动。pid负责存储游戏运行的进程id,在互相通讯的时候需要用到。
player中的字段的含义从表面上基本都可以看得到,game本意是存储在各个小游戏中的数据,但到目前为止,没有用到,说不定在教程中写着写着就用到了。socket,online什么的,其实是沿用openpoker的,因为当时可参考的资料很少,就从它里面抽出了部分代码。
client,是服务器的整体结构,但并不往数据库中写。定义在这里只是防止其他文件需要。
以上的代码内容,存储在schema.hrl中。是时候出去买早点和叫醒小懒猫了。

30三/100

一起做网游吧【2】:约定

这次接着上篇,上篇谈到目标,以及解决的方案。上次的程序模块的结构图像是由erlang自带的,appmon:start().便可以看到那个结构图像。
这次来谈下通讯的协议,数据封包的格式,关于数据封包的重要性,可以参考很早之前的外挂方面的教程,简单来说,封包协议就是客户端和服务器端通讯的语言,语言不对,将无法交流。
这个网游的协议,对于发送方和接受方,都只接受下面的封包格式:
【封包长度:4字节】【命令:4字节】【数据:不定长字节】
其实,封包长度和命令没必要使用4个字节,但我还是喜欢4个字节,看着习惯,比较好数。
对于数据中的字符串,按照下面的排列格式:
【字符串长度:4字节】【字符串:不定长字节】
对于数据的发送,分下面的情况:
1、如果客户端发送一个命令,服务器没有返回,则认为该命令发送成功,否则,服务器将会发送错误命令或者强制部分重置的命令及数据。
2、如果客户端发送一个命令,服务器返回相同的命令,认为是成功。比如发送一个登陆命令,服务器将返回登陆命令,及玩家的详细信息。
在任何时刻,程序都需要监视错误的命令。
以上约定,贯穿于整个项目。

29三/100

一起做网游吧【1】

很早之前写过网络游戏的外挂教程,现在,将网络游戏服务器教程提供出来。每个游戏商制作网游使用的技术都是不一样的,但我似乎已经习惯了孤独的写程序了,或者我的程序已经习惯了孤独了,看上去都没什么人采用这些技术(据我所知),因此,我希望我的这个游戏的整个代码(服务器和客户端)可以一个人的能力独立的完成,从目前的角度来说,是这样的。
如果某个人告诉你一个人独立完成网络游戏的服务器端和客户端,是一件遥不可及的事情,那么,现在,可以忘记这一切了。我的目标是写一个一个人可以完成的网络游戏,从目前角度和进步来说,这是一件可以完成的事情,如果你非常熟悉的话,完成一个中型的也不成问题。
关于这个教程中提供的游戏的历史:
1.这个代码从很早之前,我刚开始接触erlang的时候已经有此计划了,大约在两年前(2007年10月份)
2.这个项目中的代码(包含这个项目的教程)遵从GPL版权,不管是客户端还是服务器端,均遵从最新的GPL版权。
3.这个项目中的代码,经过很多次的变更,有些时候,随着我的兴趣不同而变更,我希望目前的游戏可以持续下去,目前来看,也比较可能最终完成。
4.项目持续的时间很长,并不是因为代码量的关系,而是因为很多时间,我都比较忙,无时间铺代码,或者心情不好等等,最长的中断时间大约至少达半年之久。
5.项目服务器端使用erlang与C++描写,如果你对erlang不感兴趣,以后的也不用关注了。
6.目前贡献的代码,是目前进度的代码根据回忆,进行整理的,因为我也觉得有必要整理了,里面很多的缺少注释的地方也应该补全了。这样,我也可以稍作休息,因为贴代码的速度比写代码的速度要快。而且,开始长时间中断的概率较少。
下面是正文的开始:

Page 1 of 11