第八章:L2JMobius学习 – 游戏服务GameServer讲解

这篇具有很好参考价值的文章主要介绍了第八章:L2JMobius学习 – 游戏服务GameServer讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本章节我们来讲解GameServer服务,首先来查看它的文件结构

第八章:L2JMobius学习 – 游戏服务GameServer讲解,L2JMobius,L2JMobius

ai:游戏角色自动化处理,比如说,自动攻击。
cache:数据缓存,里面就一个HtmCache.java类,缓存HTML文件内容。
communitybbs:bbs的管理。
data:对游戏数据的管理,涉及文件和数据库的操作。
enums:枚举类型,都是与游戏数据相关的,例如职业枚举数据。
geoengine:地图数据管理,例如三维场景下高度数据的获取。
handler:处理器,例如使用技能的效果处理。
instancemanager:各种类实例化管理,比如游戏对象实例化。
model:游戏业务模型,比如各种游戏对象类的实现。
network:网络数据通信,继承commons\network下的接口或父类
script:游戏脚本的管理
scripting:游戏脚本的管理
taskmanager:定时任务的管理
ui:图形界面目录,我们一般不使用它。
util:工具栏,一些辅助性功能的实现。
GameServer.java:GameServer服务启动入口,我们之前就是其他的它。
LoginServerThread.java:与LoginServer保持通信的线程
Shutdown.java:关闭GameServer服务

接下来,我们还是先研究network网络数据通信部分。

第八章:L2JMobius学习 – 游戏服务GameServer讲解,L2JMobius,L2JMobius

clientpackets:玩家客户端发送过来的数据包(继承ClientPacket接口)。
serverpackets:发送给玩家客户端的数据包(继承ServerPacket,继承WritablePacket)。

ClientPackets.java:客户端数据包数据枚举(根据ID实例化clientpackets数据包)。
ExClientPackets.java:客户端数据包数据枚举(解决ID数值超出byte范围问题)
ServerPackets.java:服务器数据包枚举(用来标记serverpackets的ID)
SystemMessageId.java:发送给客户端的消息ID

PacketLogger.java:数据包日志类。
PacketHandler.java:数据包处理器,继承PacketHandlerInterface接口。

BlowFishKeygen.java:秘钥生成(加密和解密数据包)
Encryption.java:加密解密类,继承EncryptionInterface接口。
GameClient.java:玩家客户端类,继承NetClient类。

ClientString.java:一个自定义的注解
ConnectionState.java:玩家客户端连接状态枚举。

loginserverpackets:发送给LoginServer服务的数据包(不讲解)。

接下来,我们介绍GameServer与玩家客户端之间的通信。当我们启动GameServer服务的时候,他会启动一个NetServer服务类,这个类我们之前已经讲过了。它里面有一个ServerSocketChannel,用来监听本机的7777端口。当有玩家客户端连接到GameServer的时候,我们就创建一个GameClient类,它代表了玩家客户端,继承自NetClient类。这个类非常重要,它会持有加密和解密的Encryption类,当然我们依然可以通过他的sendPacket方法向玩家客户端发送数据包。NetServer收到客户端发送的数据包之后,就会调用PacketHandler的handle方法来进行处理,这个类继承自PacketHandlerInterface接口。处理的方式就是,读取数据包的第一个字节,它就代表了数据包的ID。每一个数据包都有一个唯一标识ID,根据这个ID我们就能与真正的“游戏业务数据包”对应上了。这个对应关系是在ClientPackets和ServerPackets两个枚举类里面实现的。

这里,我们简单列举几个clientpackets游戏业务数据包

ProtocolVersion.java:客户端请求密钥,ID为0x00
AuthLogin.java:登录游戏服务器,返回玩家的角色列表,ID为0x08
NewCharacter.java:进入创建角色界面,发送角色模板数据,ID为0x0E(14)
CharacterCreate.java:创建并保存新角色,ID为0x0B (11)
CharacterSelected.java:选择角色,ID为0x0D (13)
EnterWorld.java:进入游戏世界,ID为0x03
Logout.java:退出游戏,ID为0x09

我们再来看一些serverpackets游戏业务数据包吧。

KeyPacket.java:向客户端发送密钥,ID为0x00
CharSelectInfo.java:返回玩家的角色列表,ID为0x13(19)
---------------------------------------------------------------------------
CharTemplates.java:角色模板,用于新建角色,ID为0x17(23)
CharCreateOk.java:创建角色成功,ID为0x19(25)
CharCreateFail.java:创建角色失败,ID为0x1A(26)
---------------------------------------------------------------------------
CharSelected.java:返回选中的角色信息,准备进入游戏世界,ID为0x15(21)
UserInfo.java:进入游戏世界,发送角色主要信息,ID为0x04
LeaveWorld.java:退出游戏世界,ID为0x7E(126)

我们上面已经说明了,每一个数据包的第一个字节代表了该数据包的唯一标识ID(上展示的ID都是十六进制)。当然,还有很多很多的数据包。我们获取这个ID之后,就能知道他对应的是哪个“游戏业务数据包”。这个是根据ClientPackets.java,ExClientPackets.java和ServerPackets.java枚举类型来定义对应关系的。这里需要单独说明一下ClientPackets.java和ExClientPackets.java的关系,他们两个都是clientpackets游戏业务数据包的ID。只不过后者是为了解决ID数值超出Byte字节大小的问题。因为我们的clientpackets非常的多,以至于它必然会超出byte自己的大小范围。于是,我们就规定当ID = 0xD0的时候,我们就继续读取数据包中下一个ID的数值,使用它来继续确定是哪个clientpackets游戏业务数据包。那么这个第二个ID数值中个对应的数据包就由ExClientPackets.java枚举来确定了。

接下来,我们继续研究PacketHandler的handle方法,如何处理clientpackets游戏业务数据包。首先是读取数据包中的第一个字节数据

final int packetId;
packetId = packet.readByte();

根据ID找到枚举类型,也就是对应的clientpackets游戏业务数据包

final ClientPackets packetEnum = ClientPackets.PACKET_ARRAY[packetId];

然后就可以实例化了

final ClientPackets packetEnum = ClientPackets.PACKET_ARRAY[packetId];

虽然声明的是ClientPacket接口类型,但是实际上就是clientpackets游戏业务数据包。接下来,我们就使用线程池技术来执行ClientPacket里面的read和run方法。这两个方法,前者是进行Byte数组数据转化类属性变量的,后者则是执行具体的游戏业务代码。如果需要向客户端发送数据包的话,也是在这个run方法中执行的。

ThreadPool.execute(new ExecuteTask(client, packet, newPacket, packetId));

我们可以查看一下ExecuteTask任务内容

_newPacket.read(_packet);
_newPacket.run(_client);

就是依次执行了read方法和run方法。当ID值为0xD0的时候,我们就会实例化ExPacket这个数据包。它不是一个游戏业务数据包。我们查看它的read方法

final int exPacketId = packet.readShort() & 0xFFFF;
_packetEnum = ExClientPackets.PACKET_ARRAY[exPacketId];
_newPacket = _packetEnum.newPacket();
_newPacket.read(packet);

看到了吧,它实际是继续读取下一个ID数值,在根据这个ID数值去ExClientPackets.java枚举中找真正的游戏业务数据包。找到之后,就实例化newPacket,然后执行实例化后newPacket的read方法。然后在run方法中,也是同样执行实例化后newPacket的run方法。也就是说,我们对应游戏的处理,就重点查看clientpackets游戏业务数据包中的run方法就行了。如果需要向客户端返回serverpackets游戏业务数据包,也是在这里执行的。这里需要注意的是,serverpackets中的数据包是直接实例化的,而它的ID则是由ServerPackets.java枚举来提供的。这一点大家要明白。

接下来,我们就来根据数据包来大致介绍一下GameServer与玩家客户端的数据通信。首先,我们仍然是先对GameClient进行实例化,这个没有太多的业务代码。然后,玩家客户端会请求ProtocolVersion数据包,该数据包中包含了客户端的版本号,然后我们在run方法中向客户端返回KeyPacket数据包,这个数据包里面包含的就是加密和解密的秘钥。有了秘钥,客户端和服务器端才能进行数据通信。

client.setProtocolVersion(_version);
client.sendPacket(new KeyPacket(client.enableCrypt(), 1));

这个KeyPacket数据包还是比较简单的,首先是他的构造方法

public KeyPacket(byte[] key, int result)
{
    _key = key;
    _result = result;
}

就是将传递过来的数据,赋值给自己类的属性变量上面。接下来就是write方法。

ServerPackets.KEY_PACKET.writeId(this);
writeByte(_result); 
for (int i = 0; i < 8; i++)
{
    writeByte(_key[i]);
}
writeInt(Config.PACKET_ENCRYPTION);
writeInt(Config.SERVER_ID);
writeByte(1);

我们不用过多的理解返回客户端的Byte数据中的所有详细内容。因为这些数据是让客户端程序来解读的。我们看到的第一句代码,就是从ServerPackets.java枚举中获取ID。

接下来,客户端获取了秘钥之后,就会继续发送AuthLogin请求数据包,里面包含了会话SessionKey数据对象和账号信息。然后在run方法中,会与LoginServer进行通信,告诉有玩家登录游戏了。然后GameServer收到LoginServer的回复之后,会想客户端发送CharSelectInfo数据包。这个数据包就是从数据库中查询玩家的所有游戏角色,然后玩家选择其中一个角色,就可以进入游戏世界了。当然,由于我们是第一次运行程序,因此,我们是没有角色的。所以,我们需要创建游戏角色。因此,我们需要在游戏客户端里面点击“创建角色”的按钮,进入到创建角色的界面。此时,客户端会向服务端发送NewCharacter数据包,这个数据包会返回客户端CharTemplates数据包,这个数据包包含了游戏的基础职业信息(人类法师和战士,精灵法师和战士等等)。关于如何创建角色,我们下一个章节介绍。

本章节涉及的内容均已上传百度网盘:

https://pan.baidu.com/s/1XdlcCFPvXnzfwFoVK7Sn7Q?pwd=avd4

欢迎加企鹅交流裙:874700842(裙文件里面也可以下载所有内容)。文章来源地址https://www.toymoban.com/news/detail-541389.html

到了这里,关于第八章:L2JMobius学习 – 游戏服务GameServer讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Rust】Rust学习 第八章常见集合

    Rust 标准库中包含一系列被称为  集合 ( collections )的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就已知,并且还可以随

    2024年02月13日
    浏览(29)
  • python学习笔记:第八章继承与超类

    与java类似,继承的出现是为了提高代码的重复利用率,避免多次输入同样的代码。而超类就是java中的父类。 要指定超类,可在定义类时,在class语句中的类名后加上超类名 基类就是超类,派生类就是子类 格式 子类会 重新定义重写超类方法init 继承超类的方法,无需再次编

    2024年02月15日
    浏览(31)
  • 统计学习导论(ISLR) 第八章树模型课后习题

    🌸个人主页:JOJO数据科学 📝个人介绍: 统计学top3 高校统计学硕士在读 💌如果文章对你有帮助,欢迎✌ 关注 、👍 点赞 、✌ 收藏 、👍 订阅 专栏 ✨本文收录于【R语言数据科学】 本系列主要介绍R语言在数据科学领域的应用包括: R语言编程基础、R语言可视化、R语言进

    2024年02月12日
    浏览(29)
  • OpenGL超级宝典第八章学习笔记:基元处理之曲面细分

    前言 本篇在讲什么 OpenGL蓝宝书第八章学习笔记之曲面细分 本篇适合什么 适合 初学OpenGL 的小白 本篇需要什么 对 C++ 语法有简单认知 对 OpenGL 有简单认知 最好是有 OpenGL超级宝典 蓝宝书 依赖 Visual Studio 编辑器 本篇的特色 具有全流程的 图文教学 重实践,轻理论,快速上手

    2024年02月07日
    浏览(31)
  • d2l_第八章学习_现代卷积神经网络

    参考: d2l 研究人员认为: 更大更干净的 数据集 或是稍加改进的特征提取方法,比任何学习算法带来的进步大得多。 认为特征本身应该被学习,即卷积核参数应该是可学习的。 创新点在于GPU与更深的网络,使用ReLU激活函数,Dropout层。 可参考: AlexNet https://blog.csdn.net/qq_4

    2024年02月11日
    浏览(34)
  • 第八章 Gateway网关

    gitee:springcloud_study: springcloud:服务集群、注册中心、配置中心(热更新)、服务网关(校验、路由、负载均衡)、分布式缓存、分布式搜索、消息队列(异步通信)、数据库集群、分布式日志、系统监控链路追踪。 1. 概述简介 官网:Spring Cloud Gateway Gateway该项目提供了一个构

    2024年02月04日
    浏览(34)
  • 第八章,帖子列表

     

    2024年02月11日
    浏览(28)
  • 第八章 常见Linux命令

    1 了解Linux帮助类命令 2 熟悉开关机命令 3 熟练文件目录类命令 4 熟悉时间日期类命令 5 熟悉用户管理命令 6 熟悉组管理命令 7 熟练文件权限命令 8 熟悉搜索查找类命令 9 熟练压缩和解压缩命令 10 熟悉磁盘分区类命令 11 熟练进程线程类命令 12 了解系统定时任务命令 man获取帮

    2024年02月11日
    浏览(28)
  • 第八章:多线程

    目录 八:多线程 8.1:基本概念 8.2:线程的创建与使用         8.2.1:Thread类的有关方法         8.2.2:线程的调度         8.2.3:两种创建线程方式的比较 8.3:线程的生命周期 8.4:线程的同步         8.4.1:同步代码块同步方法         8.4.2:单例模式的懒汉式修改

    2024年02月09日
    浏览(24)
  • 第八章:Linux信号

    linux信号是OS的重要功能。 使用kill -l查看所有信号。使用信号时,可使用信号编号或它的宏。 1、Linux中信号共有61个,没有0、32、33号信号。 2、【1,31】号信号称为普通信号,【34,64】号信号称为实时信号。 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h中

    2024年02月13日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包