传奇sf,传奇私服发布网,新开一秒传奇
当前位置:网站首页 > 传奇私服 > 正文

mir2源码详解之服务端

作者:admin发布时间:2021-08-27分类:传奇私服浏览:62评论:3


导读:传奇这款游戏一直对我的影响很大。当年为了玩传奇逃课被老师叫过N次家长。言归正传网上有很多源码当然了都是的。并且很多源码还不全由于一直学习的c、c。还真不懂。无奈硬着头皮上。好了。废...

mir2源码详解之服务端

传奇这款游戏一直对我的影响很大。当年为了玩传奇逃课被老师叫过N次家长。言归正传网上有很多源码当然了都是的。并且很多源码还不全

由于一直学习的c、c。还真不懂。无奈硬着头皮上。好了。废话不多说。开始。

 

登录网关负责游戏最开始的登录处理与账户服务器通讯。验证登录器输入的账户密码是否正确。

界面上的控件很多。其实干活的就就三个“”、“”、“”这三个控件。

 

负责与登录器进行通讯它做的操作

1、接收连接代码如下

{函数功能接受客户端连接发送消息到登录服务器}(;Socket:TCustomWinSocket);varUserSession:pTUserSession;sRemoteIPaddr,sLocalIPaddr:String;nSockIndex:Integer;IPaddr:pTSockaddr;beginSocket.nIndex:-1;//客户端IP地址sRemoteIPaddr:Socket.RemoteAddress;ifg_boDynamicIPDisModethenbeginsLocalIPaddr:ClientSocket.Socket.RemoteAddress;endelsebeginsLocalIPaddr:Socket.LocalAddress;end;//过滤ipifIsBlockIP(sRemoteIPaddr)thenbeginMainOutMessage(过滤连接:sRemoteIPaddr,1);Socket.Close;exit;end;//当前IP是否可以连接ifIsConnLimited(sRemoteIPaddr)thenbegincaseBlockMethodof//断开mDisconnect:beginSocket.Close;end;//动态过滤mBlock:beginNew(IPaddr);IPaddr.nIPaddr:inet_addr(PChar(sRemoteIPaddr));TempBlockIPList.Add(IPaddr);CloseConnect(sRemoteIPaddr);end;//永久过滤mBlockList:beginNew(IPaddr);IPaddr.nIPaddr:inet_addr(PChar(sRemoteIPaddr));BlockIPList.Add(IPaddr);CloseConnect(sRemoteIPaddr);end;end;MainOutMessage(端口攻击:sRemoteIPaddr,1);exit;end;//如果网关准备好了ifboGateReadythenbeginfornSockIndex:0toGATEMAXSESSION-1dobeginUserSession:g_SessionArray[nSockIndex];ifUserSession.SocketnilthenbeginUserSession.Socket:Socket;UserSession.sRemoteIPaddr:sRemoteIPaddr;UserSession.nSendMsgLen:UserSession.bo0C:False;UserSession.dw10Tick:GetTickCount();UserSession.dwConnctCheckTick:GetTickCount();UserSession.boSendAvailable:True;UserSession.boSendCheck:False;UserSession.nCheckSendLength:UserSession.n20:UserSession.dwUserTimeOutTick:GetTickCount();UserSession.SocketHandle:Socket.SocketHandle;UserSession.sIP:sRemoteIPaddr;UserSession.MsgList.Clear;Socket.nIndex:nSockIndex;Inc(nSessionCount);break;end;end;//和本地登录服务器进行通讯ifSocket.nIndex0thenbeginClientSocket.Socket.SendText(%OIntToStr(Socket.SocketHandle)/sRemoteIPaddr/sLocalIPaddr$MainOutMessage(Connect:sRemoteIPaddr,5);endelsebeginSocket.Close;MainOutMessage(KickOff:sRemoteIPaddr,1);end;endelsebegin//0x004529EFSocket.Close;MainOutMessage(KickOff:sRemoteIPaddr,1);end;end;

 说白了别看那些IP过滤规则和连接限制什么的。就是来了一用户直接保存到一个UserSession中。然后通知LoginSvr有人连接了。。现在市面上的什么GOM引擎、Hero、HGE原3K、IGE、Legend、GEEM2、77M2都是换汤不换药。变的就是加密方式。这里不得不说JsocketJ就是TServerSocket、TClientSocket的控件懒得看源码看了下其属性大致就可以了解到。

是采用的线程池的select模型。早期的游戏都采用这种方式不过对于私*服连接量不大完全足够应付。其实有更好的解决办法那就是IOCP来管理。效率更高。windows下最适合的模型了。

2、断开连接代码如下

procedureTFrmMain.ServerSocketClientDisconnect(Sender:TObject;Socket:TCustomWinSocket);varI:Integer;UserSession:pTUserSession;nSockIndex:Integer;sRemoteIPaddr:String;IPaddr:pTSockaddr;nIPaddr:Integer;beginsRemoteIPaddr:Socket.RemoteAddress;nIPaddr:inet_addr(PChar(sRemoteIPaddr));nSockIndex:Socket.nIndex;forI:0toCurrIPaddrList.Count-1dobeginIPaddr:CurrIPaddrList.Items[I];ifIPaddr.nIPaddrnIPaddrthenbeginDec(IPaddr.nCount);ifIPaddr.nCount0thenbeginDispose(IPaddr);CurrIPaddrList.Delete(I);end;Break;end;end;if(nSockIndex0)and(nSockIndexGATEMAXSESSION)thenbeginUserSession:g_SessionArray[nSockIndex];UserSession.Socket:nil;UserSession.sRemoteIPaddr:UserSession.SocketHandle:-1;UserSession.MsgList.Clear;Dec(nSessionCount);ifboGateReadythenbeginClientSocket.Socket.SendText(%XIntToStr(Socket.SocketHandle)$MainOutMessage(DisConnect:sRemoteIPaddr,5);end;end;end;

 删除释放并通知LoginSvr有人断开连接了。。。

3、接收数据代码如下

procedureTFrmMain.ServerSocketClientRead(Sender:TObject;Socket:TCustomWinSocket);varUserSession:pTUserSession;nSockIndex:Integer;sReviceMsg,s10,s1C:String;nPos:Integer;nMsgLen:Integer;beginnSockIndex:Socket.nIndex;if(nSockIndex0)and(nSockIndexGATEMAXSESSION)thenbeginUserSession:g_SessionArray[nSockIndex];sReviceMsg:Socket.ReceiveText;if(sReviceMsg)and(boServerReady)thenbeginnPos:Pos(*,sReviceMsg);ifnPos0thenbeginUserSession.boSendAvailable:True;UserSession.boSendCheck:False;UserSession.nCheckSendLength:s10:Copy(sReviceMsg,1,nPos-1);s1C:Copy(sReviceMsg,nPos1,Length(sReviceMsg)-nPos);sReviceMsg:s10s1C;end;nMsgLen:length(sReviceMsg);if(sReviceMsg)and(boGateReady)and(notboKeepAliveTimcOut)thenbeginUserSession.dwConnctCheckTick:GetTickCount();if(GetTickCount-UserSession.dwUserTimeOutTick)1000thenbeginInc(UserSession.n20,nMsgLen);endelseUserSession.n20:nMsgLen;ClientSocket.Socket.SendText(%AIntToStr(Socket.SocketHandle)/sReviceMsg$end;end;end;end;

 拿到数据转发到登录账户服务器。。。。。它主要干的事完了。。

 

ClientSocket负责与LoginSvr进行通讯它做的主要工作就是

procedureTFrmMain.ClientSocketRead(Sender:TObject;Socket:TCustomWinSocket);varsRecvMsg:String;beginsRecvMsg:Socket.ReceiveText;ClientSockeMsgList.Add(sRecvMsg);end;

 你没有看错就是接收到数据。然后保存到链表然后等待定时器来解析操作。。

 

DecodeTimer定时器的工作。源码中设置的时间精度是1毫秒

procedureTFrmMain.DecodeTimerTimer(Sender:TObject);varsProcessMsg:String;sSocketMsg:String;sSocketHandle:String;nSocketIndex:Integer;nMsgCount:Integer;nSendRetCode:Integer;nSocketHandle:Integer;dwDecodeTick:LongWord;dwDecodeTime:LongWord;sRemoteIPaddr:String;UserSession:pTUserSession;IPaddr:pTSockaddr;beginShowMainLogMsg();ifboDecodeLockor(notboGateReady)thenexit;trydwDecodeTick:GetTickCount();boDecodeLock:True;sProcessMsg:while(True)dobeginifClientSockeMsgList.Count0thenbreak;sProcessMsg:sProcMsgClientSockeMsgList.Strings[0];sProcMsg:ClientSockeMsgList.Delete(0);while(True)dobeginifTagCount(sProcessMsg,$)1thenbreak;sProcessMsg:ArrestStringEx(sProcessMsg,%,$,sSocketMsg);ifsSocketMsgthenbreak;ifsSocketMsg[1]thenbeginifsSocketMsg[2]-thenbeginCloseSocket(Str_ToInt(Copy(sSocketMsg,3,Length(sSocketMsg)-2),0));Continue;endelsebegin//0x004521B7dwKeepAliveTick:GetTickCount();boKeepAliveTimcOut:False;Continue;end;end;//0x004521CDsSocketMsg:GetValidStr3(sSocketMsg,sSocketHandle,[/]);nSocketHandle:Str_ToInt(sSocketHandle,-1);ifnSocketHandle0thenContinue;fornSocketIndex:0toGATEMAXSESSION-1dobeginifg_SessionArray[nSocketIndex].SocketHandlenSocketHandlethenbeging_SessionArray[nSocketIndex].MsgList.Add(sSocketMsg);break;end;end;end;//0x00452246end;//0x452252//ifsProcessMsgthenClientSockeMsgList.Add(sProcessMsg);ifsProcessMsgthensProcMsg:sProcessMsg;nSendMsgCount:n456A2C:StringList318.Clear;fornSocketIndex:0toGATEMAXSESSION-1dobeginifg_SessionArray[nSocketIndex].SocketHandle-1thenContinue;//踢除超时无数据传输连接if(GetTickCount-g_SessionArray[nSocketIndex].dwConnctCheckTick)dwKeepConnectTimeOutthenbeginsRemoteIPaddr:g_SessionArray[nSocketIndex].sRemoteIPaddr;caseBlockMethodof//mDisconnect:beging_SessionArray[nSocketIndex].Socket.Close;end;mBlock:beginNew(IPaddr);IPaddr.nIPaddr:inet_addr(PChar(sRemoteIPaddr));TempBlockIPList.Add(IPaddr);CloseConnect(sRemoteIPaddr);end;mBlockList:beginNew(IPaddr);IPaddr.nIPaddr:inet_addr(PChar(sRemoteIPaddr));BlockIPList.Add(IPaddr);CloseConnect(sRemoteIPaddr);end;end;MainOutMessage(端口空连接攻击:sRemoteIPaddr,1);Continue;end;while(True)dobegin;ifg_SessionArray[nSocketIndex].MsgList.Count0thenbreak;UserSession:g_SessionArray[nSocketIndex];nSendRetCode:SendUserMsg(UserSession,UserSession.MsgList.Strings[0]);if(nSendRetCode0)thenbeginifnSendRetCode1thenbeginUserSession.dwConnctCheckTick:GetTickCount();UserSession.MsgList.Delete(0);Continue;end;ifUserSession.MsgList.Count100thenbeginnMsgCount:whilenMsgCount51dobeginUserSession.MsgList.Delete(0);Inc(nMsgCount);end;end;Inc(n456A2C,UserSession.MsgList.Count);MainOutMessage(UserSession.sIP:IntToStr(UserSession.MsgList.Count),5);Inc(nSendMsgCount);endelsebegin//0x004523A4UserSession.SocketHandle:-1;UserSession.Socket:nil;UserSession.MsgList.Clear;end;end;end;if(GetTickCount-dwSendKeepAliveTick)2*1000thenbegindwSendKeepAliveTick:GetTickCount();ifboGateReadythenClientSocket.Socket.SendText(%--$end;if(GetTickCount-dwKeepAliveTick)10*1000thenbeginboKeepAliveTimcOut:True;ClientSocket.Close;end;finallyboDecodeLock:False;end;dwDecodeTime:GetTickCount-dwDecodeTick;ifdwDecodeMsgTimedwDecodeTimethendwDecodeMsgTime:dwDecodeTime;ifdwDecodeMsgTime50thenDec(dwDecodeMsgTime,50);end;

 又是一坨代码简而言之就是把刚才保存接收到的数据。分发到每个用户的自己的消息链表中然后遍历发送出去

代码如下

//发送用户消息functionTFrmMain.SendUserMsg(UserSession:pTUserSession;sSendMsg:String):Integer;beginResult:-1;//如果ifUserSession.Socketnilthenbegin//取反ifnotUserSession.bo0Cthenbegin//如果不能发送,则置可用ifnotUserSession.boSendAvailableand(GetTickCountUserSession.dwSendLockTimeOut)thenbeginUserSession.boSendAvailable:True;UserSession.nCheckSendLength:0;boSendHoldTimeOut:True;dwSendHoldTick:GetTickCount();end;//004525DDifUserSession.boSendAvailablethenbeginifUserSession.nCheckSendLength250thenbeginifnotUserSession.boSendCheckthenbeginUserSession.boSendCheck:True;sSendMsg:*sSendMsg;end;ifUserSession.nCheckSendLength512thenbeginUserSession.boSendAvailable:False;UserSession.dwSendLockTimeOut:GetTickCount3*1000;end;end;//00452620UserSession.Socket.SendText(sSendMsg);Inc(UserSession.nSendMsgLen,length(sSendMsg));Inc(UserSession.nCheckSendLength,length(sSendMsg));Result:1;endelsebegin//0x0045264AResult:0;end;endelsebegin//0x00452651Result:0;end;end;end;

 登录网关是不是很简单这不是重点重点是市面上的很多引擎的登录网关都基于这套机制只需要逆向分析下其加密算法一个自定义网关则出来了。至于过滤规则什么IP通道都是浮云。。。。

 


已有3位网友发表了看法:

欢迎 发表评论: