当前位置: 首页 > news >正文

兰州做it网站运营的怎么样网站系统说明书

兰州做it网站运营的怎么样,网站系统说明书,百度平台商户电话号码,wordpress 五分钟专题介绍 该专题将会分析 LOMCN 基于韩版传奇 2#xff0c;使用 .NET 重写的传奇源码#xff08;服务端 客户端#xff09;#xff0c;分析数据交互、状态管理和客户端渲染等技术#xff0c;此外笔者还会分享将客户端部分移植到 Unity 和服务端用现代编程语言重写的全过…专题介绍 该专题将会分析 LOMCN 基于韩版传奇 2使用 .NET 重写的传奇源码服务端 客户端分析数据交互、状态管理和客户端渲染等技术此外笔者还会分享将客户端部分移植到 Unity 和服务端用现代编程语言重写的全过程。 概览 在这一篇文章中我们将从客户端入手分析从 TCP 连接建立、登录鉴权、角色选择、开始游戏到游戏内交互的全过程。 客户端启动 WinForm 入口 Program.cs 与服务端类似客户端也是一个 WinForm 应用程序在 Application 启动后会先跳转到 AMain 检查是否有热更新随后再跳转到 CMain 开启客户端主逻辑 // Program.cs [STAThread] private static void Main(string[] args) {// ...Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);if (Settings.P_Patcher) Application.Run(PForm new Launcher.AMain());else Application.Run(Form new CMain());// ... }监听事件循环 在 CMain 的构造函数中我们监听了 Application Idle 事件作为事件循环 // CMain.cs public CMain() {InitializeComponent();Application.Idle Application_Idle;// ... }在 Application_Idle 中我们通过 UpdateTime 更新客户端全局的时间戳通过 UpdateEnviroment 处理网络数据通过 RenderEnvironment 处理客户端渲染 private static void Application_Idle(object sender, EventArgs e) {try{while (AppStillIdle){UpdateTime();UpdateEnviroment();RenderEnvironment();}}catch (Exception ex){SaveError(ex.ToString());} }客户端场景划分 在用户登录之前UpdateEnviroment 发现连接实例为空不会做任何操作因此我们先跳过这个函数来看 RenderEnvironment 的处理过程这里实际上就是基于 Direct 3D 的客户端的渲染循环请大家注意 MirScene.ActiveScene.Draw 这个调用传奇通过 Scene 去区分不同的场景例如登录页面、角色选择页面和游戏页面每个页面都是一个独立的 Scene private static void RenderEnvironment() {try{if (DXManager.DeviceLost){DXManager.AttemptReset();Thread.Sleep(1);return;}DXManager.Device.Clear(ClearFlags.Target, Color.CornflowerBlue, 0, 0);DXManager.Device.BeginScene();DXManager.Sprite.Begin(SpriteFlags.AlphaBlend);DXManager.SetSurface(DXManager.MainSurface);// Note hereif (MirScene.ActiveScene ! null)MirScene.ActiveScene.Draw();DXManager.Sprite.End();DXManager.Device.EndScene();DXManager.Device.Present();}catch (Direct3D9Exception ex){DXManager.DeviceLost true;}catch (Exception ex){SaveError(ex.ToString());DXManager.AttemptRecovery();} }那么当前的 ActiveScene 是在哪里设置的呢实际上在 MirScene 初始化时它会被指定为 LoginScene public abstract class MirScene : MirControl {public static MirScene ActiveScene new LoginScene();// ... }因此上面的 Draw 方法其实会将登录页面绘制出来我们这里先跳过 GUI 相关的部分直接来看一下当用户输入完账号密码后是如何建立连接和发起登录的。 TCP 连接建立 传奇中的每个 Scene 都是继承自 MirControl 的 UI 对象MirControl 提供了 Shown 回调用于监听 UI 的展示在 LoginScene 展示时我们会开启 TCP 连接 public LoginScene() {// ...Shown (sender, args) {Network.Connect();_connectBox.Show();}; }Network 是客户端的网络管理类在 Connect 方法中我们会创建一个 TcpClient 对象并发起连接服务端的信息通过配置获取 public static void Connect() {if (_client ! null)Disconnect();ConnectAttempt;_client new TcpClient {NoDelay true};_client.BeginConnect(Settings.IPAddress, Settings.Port, Connection, null); }与服务端的处理方式类似在 BeginConnect 的异步回调中我们会开启 receiveList 和 sendList 两个队列然后通过 BeginReceive 接收服务端数据、处理成 Packet 并加入 receiveList 等待处理。在客户端每帧 Process 的过程中我们会处理 receiveList 更改客户端状态同时根据用户输入产生数据包加入到 sendList 发送到服务端。 第一个数据包 服务端发送 S.Connected 通过上面的分析我们知道客户端启动的第一步是发起 TCP 连接请求服务端在对 Client 进行 Accept 时会创建 MirConnection 对象如果对此没有印象可以参考第一篇文章在 MirConnection 的构造方法中我们会向客户端发送 Connected 数据包这便是客户端与服务端交流的第一个数据包啦 public MirConnection(int sessionID, TcpClient client) {// ..._receiveList new ConcurrentQueuePacket();_sendList new ConcurrentQueuePacket();_sendList.Enqueue(new S.Connected());_retryList new QueuePacket();Connected true;BeginReceive(); }客户端处理 S.Connected 前面我们提到在 TCP 连接建立之前基于 Application Idle 的事件循环对 UpdateEnviroment 的调用会被忽略而在连接建立之后这里会通过 Network.Process 处理服务端数据包和发送这一帧产生的数据包数据包会被路由到 ActiveScene 进行处理因此这里的 ProcessPacket 会调用到 LoginScene public static void Process() {// ...while (_receiveList ! null !_receiveList.IsEmpty){if (!_receiveList.TryDequeue(out Packet p) || p null) continue;MirScene.ActiveScene.ProcessPacket(p);}if (CMain.Time TimeOutTime _sendList ! null _sendList.IsEmpty)_sendList.Enqueue(new C.KeepAlive());if (_sendList null || _sendList.IsEmpty) return;TimeOutTime CMain.Time Settings.TimeOut; // 5000msListbyte data new Listbyte();while (!_sendList.IsEmpty){if (!_sendList.TryDequeue(out Packet p)) continue;data.AddRange(p.GetPacketBytes());}CMain.BytesSent data.Count;BeginSend(data); }在 LoginScene 的 ProcessPacket 中包含了对客户端初始化和账户相关的数据处理由于当前数据包是 S.Connected 自然会进入到 ServerPacketIds.Connected 这个 case随后客户端通过 SendVersion 发送数据完整性检查请求这里会对 Executable 进行 hash public override void ProcessPacket(Packet p) {switch (p.Index){case (short)ServerPacketIds.Connected:Network.Connected true;SendVersion();break;case (short)ServerPacketIds.ClientVersion:ClientVersion((S.ClientVersion) p);break;// ...default:base.ProcessPacket(p);break;} }数据完整性检查与 Connected 数据包类似首先客户端发送 hash 到服务端服务端校验后将结果返回到客户端这是一个初级的逆向对抗策略可通过修改发送的 hash 或忽略返回的错误跳过。 客户端登录过程 在上述检查通过以后客户端会展示账号密码输入页面用户输入账号密码后点击登录会调用 Login 方法发起登录请求 // LoginScene.cs private void Login() {OKButton.Enabled false;Network.Enqueue(new C.Login {AccountID AccountIDTextBox.Text, Password PasswordTextBox.Text}); }作为一款早期的游戏传奇的密码采用了明文传输囧服务端收到 C.Login 数据包后会尝试从 Account Database 中查询与之匹配的账户如果校验失败会发送 S.Login 返回登录失败的原因成功则发送 S.LoginSuccess // Envir.cs public void Login(ClientPackets.Login p, MirConnection c) {// ...if (!AccountIDReg.IsMatch(p.AccountID)){c.Enqueue(new ServerPackets.Login { Result 1 });return;}if (!PasswordReg.IsMatch(p.Password)){c.Enqueue(new ServerPackets.Login { Result 2 });return;}var account GetAccount(p.AccountID);if (account null){c.Enqueue(new ServerPackets.Login { Result 3 });return;}// ...if (string.CompareOrdinal(account.Password, p.Password) ! 0){if (account.WrongPasswordCount 5){account.Banned true;account.BanReason Too many Wrong Login Attempts.;account.ExpiryDate DateTime.Now.AddMinutes(2);c.Enqueue(new ServerPackets.LoginBanned{Reason account.BanReason,ExpiryDate account.ExpiryDate});return;}c.Enqueue(new ServerPackets.Login { Result 4 });return;}account.WrongPasswordCount 0;lock (AccountLock){account.Connection?.SendDisconnect(1);account.Connection c;}c.Account account;c.Stage GameStage.Select;account.LastDate Now;account.LastIP c.IPAddress;MessageQueue.Enqueue(account.Connection.SessionID , account.Connection.IPAddress , User logged in.);c.Enqueue(new ServerPackets.LoginSuccess { Characters account.GetSelectInfo() }); }相应地在客户端侧也包含了对 Login 和 LoginSuccess 的处理 // LoginScene.cs public override void ProcessPacket(Packet p) {switch (p.Index){// ...case (short)ServerPacketIds.Login:Login((S.Login) p);break;case (short)ServerPacketIds.LoginSuccess:Login((S.LoginSuccess) p);break;default:base.ProcessPacket(p);break;} }在登录失败时会调用到 private void Login(S.Login p) 这个重载方法展示登录失败原因事实上出于安全考虑登录失败的原因应当尽可能模糊 // LoginScene.cs private void Login(S.Login p) {_login.OKButton.Enabled true;switch (p.Result){case 0:MirMessageBox.Show(Logging in is currently disabled.);_login.Clear();break;case 1:MirMessageBox.Show(Your AccountID is not acceptable.);_login.AccountIDTextBox.SetFocus();break;case 2:MirMessageBox.Show(Your Password is not acceptable.);_login.PasswordTextBox.SetFocus();break;case 3:MirMessageBox.Show(GameLanguage.NoAccountID);_login.PasswordTextBox.SetFocus();break;case 4:MirMessageBox.Show(GameLanguage.IncorrectPasswordAccountID);_login.PasswordTextBox.Text string.Empty;_login.PasswordTextBox.SetFocus();break;} }在登录成功时会调用到 private void Login(S.LoginSuccess p) 这个重载方法切换到角色选择 Scene 等待用户的下一步操作为了避免额外的数据交互服务端在登录成功后会返回角色列表 // LoginScene.cs private void Login(S.LoginSuccess p) {Enabled false;_login.Dispose();if(_ViewKey ! null !_ViewKey.IsDisposed) _ViewKey.Dispose();SoundManager.PlaySound(SoundList.LoginEffect);_background.Animated true;_background.AfterAnimation (o, e) {Dispose();ActiveScene new SelectScene(p.Characters);}; }开始游戏 服务端同步角色数据 在用户选择完角色点击开始游戏后客户端会发送包含角色选择信息的 C.StartGame 数据包到服务端 // SelectScene.cs public void StartGame() {// ...Network.Enqueue(new C.StartGame{CharacterIndex Characters[_selected].Index}); }服务端在接收到 C.StartGame 后会读从数据库读取角色数据随后新建一个 PlayerObject 调用 StartGame 方法 // MirConnection.cs private void StartGame(C.StartGame p) {// ...CharacterInfo info null;for (int i 0; i Account.Characters.Count; i){if (Account.Characters[i].Index ! p.CharacterIndex) continue;info Account.Characters[i];break;}if (info null){Enqueue(new S.StartGame { Result 2 });return;}// ...Player new PlayerObject(info, this);Player.StartGame(); }在 PlayerObject 的 StartGame 方法中服务端将角色添加到地图中随后发送游戏开始和玩家数据到客户端 // PlayerObject.cs public void StartGame() {Map temp Envir.GetMap(CurrentMapIndex);if (temp ! null temp.Info.NoReconnect){Map temp1 Envir.GetMapByNameAndInstance(temp.Info.NoReconnectMap);if (temp1 ! null){temp temp1;CurrentLocation GetRandomPoint(40, 0, temp);}}if (temp null || !temp.ValidPoint(CurrentLocation)){temp Envir.GetMap(BindMapIndex);if (temp null || !temp.ValidPoint(BindLocation)){SetBind();temp Envir.GetMap(BindMapIndex);if (temp null || !temp.ValidPoint(BindLocation)){StartGameFailed();return;}}CurrentMapIndex BindMapIndex;CurrentLocation BindLocation;}temp.AddObject(this);CurrentMap temp;Envir.Players.Add(this);StartGameSuccess();//Call Login NPCCallDefaultNPC(DefaultNPCType.Login);//Call Daily NPCif (Info.NewDay){CallDefaultNPC(DefaultNPCType.Daily);} }随后在 StartGameSuccess 的调用中向客户端发送游戏开发和角色数据这里的每个 Get 方法的作用都是将地图和角色数据同步到客户端 // PlayerObject.cs private void StartGameSuccess() {Connection.Stage GameStage.Game;// ...Enqueue(new S.StartGame { Result 4, Resolution Settings.AllowedResolution });ReceiveChat(string.Format(GameLanguage.Welcome, GameLanguage.GameName), ChatType.Hint);// ...Spawned();SetLevelEffects();GetItemInfo();GetMapInfo();GetUserInfo();GetQuestInfo();GetRecipeInfo();GetCompletedQuests();GetMail();GetFriends();GetRelationship();if ((Info.Mentor ! 0) (Info.MentorDate.AddDays(Settings.MentorLength) DateTime.Now))MentorBreak();elseGetMentor();CheckConquest();GetGameShop();// ... }private void GetUserInfo() {string guildname MyGuild ! null ? MyGuild.Name : ;string guildrank MyGuild ! null ? MyGuildRank.Name : ;S.UserInformation packet new S.UserInformation{ObjectID ObjectID,RealId (uint)Info.Index,Name Name,GuildName guildname,GuildRank guildrank,NameColour GetNameColour(this),Class Class,Gender Gender,Level Level,Location CurrentLocation,Direction Direction,Hair Hair,HP HP,MP MP,Experience Experience,MaxExperience MaxExperience,LevelEffects LevelEffects,Inventory new UserItem[Info.Inventory.Length],Equipment new UserItem[Info.Equipment.Length],QuestInventory new UserItem[Info.QuestInventory.Length],Gold Account.Gold,Credit Account.Credit,HasExpandedStorage Account.ExpandedStorageExpiryDate Envir.Now ? true : false,ExpandedStorageExpiryTime Account.ExpandedStorageExpiryDate};Info.Inventory.CopyTo(packet.Inventory, 0);Info.Equipment.CopyTo(packet.Equipment, 0);Info.QuestInventory.CopyTo(packet.QuestInventory, 0);//IntelligentCreaturefor (int i 0; i Info.IntelligentCreatures.Count; i)packet.IntelligentCreatures.Add(Info.IntelligentCreatures[i].CreateClientIntelligentCreature());packet.SummonedCreatureType SummonedCreatureType;packet.CreatureSummoned CreatureSummoned;Enqueue(packet); }客户端开始游戏 客户端目前处于 SelectScene在收到游戏启动成功的数据包 S.StartGame 后会根据返回数据调整分辨率并切换到 GameScene public void StartGame(S.StartGame p) {StartGameButton.Enabled true;switch (p.Result){case 0:MirMessageBox.Show(Starting the game is currently disabled.);break;case 1:MirMessageBox.Show(You are not logged in.);break;case 2:MirMessageBox.Show(Your character could not be found.);break;case 3:MirMessageBox.Show(No active map and/or start point found.);break;case 4:if (p.Resolution Settings.Resolution || Settings.Resolution 0) Settings.Resolution p.Resolution;switch (Settings.Resolution){default:case 1024:Settings.Resolution 1024;CMain.SetResolution(1024, 768);break;case 1280:CMain.SetResolution(1280, 800);break;case 1366:CMain.SetResolution(1366, 768);break;case 1920:CMain.SetResolution(1920, 1080);break;}ActiveScene new GameScene();Dispose();break;} }在 GameScene 中客户端会处理来自服务端的角色信息、地图数据以及 NPC 和其他玩家数据等例如在收到游戏开始时服务端发送的 S.UserInformation 后会创建当前玩家的角色 // GameScene.cs public override void ProcessPacket(Packet p) {switch (p.Index){// ...case (short)ServerPacketIds.UserInformation:UserInformation((S.UserInformation)p);break;// ...} }private void UserInformation(S.UserInformation p) {User new UserObject(p.ObjectID);User.Load(p);MainDialog.PModeLabel.Visible User.Class MirClass.Wizard || User.Class MirClass.Taoist;Gold p.Gold;Credit p.Credit;InventoryDialog.RefreshInventory();foreach (SkillBarDialog Bar in SkillBarDialogs)Bar.Update(); }下一步 到这里整个客户端的启动流程就分析完了接下来的逻辑主要集中在服务端向客户端同步状态和客户端发送角色行为在接下来的文章中我们将深入分析这些交互的处理过程。
http://www.zqtcl.cn/news/89734/

相关文章:

  • 宜宾建功路桥建设有限公司网站成都住建局官网电话查询
  • 优秀原创设计网站杭州有哪些网络公司
  • 珠海网站建设推广方案wordpress 设置密码
  • 军事网站大全军事网wordpress怎么做开发
  • 怎么做免费的网站空间上海做家教网站有哪些
  • 海关年检要去哪个网站上做连云港东海网站建设
  • 怎么做网站和注册域名服装网站建设优点和缺点
  • 企业网站管理系统如何使用说明2021个人网站盈利模式
  • 展示型网站系统openvz wordpress
  • 做网站最简单徐州百度seo排名
  • 重庆网站建设案例g宝盆网站建设优惠
  • 深圳企业网站推广如何在百度发布文章
  • 寮步网站建设 优帮云淮安品牌网站建设
  • 做任务的设计网站wordpress远程图片本地换
  • 可以做审计初级题的网站五大搜索引擎 三大门户网站
  • 重庆沛宣网站建设wordpress首页缓存
  • 做盗版视频网站凡客网站规划与建设ppt
  • acm网站免费做如何注册或购买一个域名使用
  • 我如何做网络推广网站如何做推广晚上必看的正能量直播app
  • 如何修改asp网站免费可以绑定域名网站空间
  • 建设厅网站合同备案在哪里关于网站开发的文档
  • 成都网站建设qghl个人网页案例
  • 免费申请网站com域名网络营销工具的案例
  • 精品网站建设公西安高端网站建设哪家好
  • 专业的网站建设设计多个wordpress共用一个数据库前缀
  • 企业网站设计的基本内容包括哪些网站开发哪里接业务
  • 广州商城网站制作网站河南省漯河建设局网站
  • 旅游类网站开发设计报告全球采购
  • 免费发布推广的网站有哪些怎么进网站后台管理系统
  • 网站权重是怎样进行传递的推广普通话主题班会ppt