unit GateShare;

interface
uses
  Windows, Messages, SysUtils, Classes, JSocket, WinSock, SyncObjs, IniFiles, Grobal2, Common;
resourcestring
  g_sVersion = 'Build: Mir Heroes';
  g_sUpDateTime = 'Updated: 24/08/2011';
  g_sProgram = 'Program: Heroes';
  g_sWebSite = 'Web: www.legendofmirheroes.com';
const
  tRunGate = 8;
  GATEMAXSESSION = 1000;
  MSGMAXLENGTH = 20000;
  SENDCHECKSIZE = 512;
  SENDCHECKSIZEMAX = 2048;

  sSTATUS_FAIL = '+FAIL/';
  sSTATUS_GOOD = '+GOOD/';
type
  TGList = class(TList)
  private
    GLock: TRTLCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Lock;
    procedure UnLock;
  end;

  TBlockIPMethod = (mDisconnect, mBlock, mBlockList);
  TSockaddr = record
    nIPaddr: Integer;
    dwStartAttackTick: LongWord;
    nAttackCount: Integer;
    nSocketHandle: Integer;
  end;
  pTSockaddr = ^TSockaddr;

  TConfig = record
    boHit: Boolean;
    boSpell: Boolean;
    boRun: Boolean;
    boWalk: Boolean;
    boTurn: Boolean;
    nHitTime: Integer;
    nSpellTime: Integer;
    nRunTime: Integer;
    nWalkTime: Integer;
    nTurnTime: Integer;
    nHitCount: Integer;
    nSpellCount: Integer;
    nRunCount: Integer;
    nWalkCount: Integer;
    nTurnCount: Integer;
    btSpeedControlMode: Byte;
    boSpeedShowMsg: Boolean;
    sSpeedShowMsg: string;
    btMsgColor: Byte;
  end;

  TGameSpeed = record
    dwHitTimeTick: LongWord;
    dwSpellTimeTick: LongWord;
    dwRunTimeTick: LongWord;
    dwWalkTimeTick: LongWord;
    dwTurnTimeTick: LongWord;
    nHitCount: Integer;
    nSpellCount: Integer;
    nRunCount: Integer;
    nWalkCount: Integer;
    nTurnCount: Integer;
  end;
  pTGameSpeed = ^TGameSpeed;

  TSessionInfo = record
    Socket: TCustomWinSocket;
    sSocData: string;
    sSendData: string;
    nUserListIndex: Integer;
    nPacketIdx: Integer;
    nPacketErrCount: Integer; //Repeat count number of data packets (client to send data with packet inspection)
    boStartLogon: Boolean;
    boSendLock: Boolean;
    boOverNomSize: Boolean;
    nOverNomSizeCount: ShortInt;
    dwSendLatestTime: LongWord;
    nCheckSendLength: Integer;
    boSendAvailable: Boolean;
    boSendCheck: Boolean;
    dwTimeOutTime: LongWord;
    nReceiveLength: Integer;
    dwReceiveLengthTick: LongWord;
    dwReceiveTick: LongWord;
    nSckHandle: Integer;
    sRemoteAddr: string;
    dwSayMsgTick: LongWord; //Speech interval control
    dwHitTick: LongWord; //Attack time
    dwSpeedingTick: LongWord;
    //GameSpeed: TGameSpeed;
  end;

  pTSessionInfo = ^TSessionInfo;
  TSendUserData = record
    nSocketIdx: Integer; //0x00
    nSocketHandle: Integer; //0x04
    sMsg: string; //0x08
  end;
  pTSendUserData = ^TSendUserData;
procedure AddMainLogMsg(Msg: string; nLevel: Integer);
procedure LoadAbuseFile();
procedure LoadBlockIPFile();
procedure SaveBlockIPList();
procedure SendGameCenterMsg(wIdent: Word; sSendMsg: string);
var
  g_Config: TConfig = (
    boHit: False;
    boSpell: False;
    boRun: False;
    boWalk: False;
    boTurn: False;
    nHitTime: 500;
    nSpellTime: 500;
    nRunTime: 300;
    nWalkTime: 300;
    nTurnTime: 200;
    nHitCount: 3;
    nSpellCount: 3;
    nRunCount: 3;
    nWalkCount: 3;
    nTurnCount: 3;
    btSpeedControlMode: 1;
    boSpeedShowMsg: True;
    sSpeedShowMsg: '[Tip]: Care for the game environment, do not use illegal plugins!';
    btMsgColor: 0;
    );

  CS_MainLog: TCriticalSection;
  CS_FilterMsg: TCriticalSection;
  MainLogMsgList: TStringList;
  nShowLogLevel: Integer = 3;
  GateClass: string = 'Server';
  GateName: string = 'Run Gate';
  TitleName: string = 'LOMCN';

  ServerAddr: string = '127.0.0.1';
  ServerPort: Integer = 5000;
  GateAddr: string = '0.0.0.0';
  GatePort: Integer = 7200;
  boStarted: Boolean = False;
  boClose: Boolean = False;
  boShowBite: Boolean = True; //Display B or KB
  boServiceStart: Boolean = False;
  boGateReady: Boolean = False; //0045AA74 Gateway is ready
  boCheckServerFail: Boolean = False; //Gateway <-> game server tests fail (timeout)
  //  dwCheckServerTimeOutTime    :LongWord = 60 * 1000 ;//Gateway <-> game server timeout length of time between the detection
  dwCheckServerTimeOutTime: LongWord = 3 * 60 * 1000; //Gateway <-> game server timeout length of time between the detection
  AbuseList: TStringList; //004694F4
  SessionArray: array[0..GATEMAXSESSION - 1] of TSessionInfo;
  SessionCount: Integer; //0x32C Connected Sessions
  boShowSckData: Boolean; //0x324 Whether to display the information received SOCKET

  sReplaceWord: string = '*';
  ReviceMsgList: TList; //0x45AA64
  SendMsgList: TList; //0x45AA68
  nCurrConnCount: Integer;
  boSendHoldTimeOut: Boolean;
  dwSendHoldTick: LongWord;
  n45AA80: Integer;
  n45AA84: Integer;
  dwCheckRecviceTick: LongWord;
  dwCheckRecviceMin: LongWord;
  dwCheckRecviceMax: LongWord;

  dwCheckServerTick: LongWord;
  dwCheckServerTimeMin: LongWord;
  dwCheckServerTimeMax: LongWord;
  SocketBuffer: PChar; //45AA5C
  g_nBuffLen: Integer; //45AA60
  List_45AA58: TList;
  boDecodeMsgLock: Boolean;
  dwProcessReviceMsgTimeLimit: LongWord;
  dwProcessSendMsgTimeLimit: LongWord;

  BlockIPList: TGList; //Ban IP list of connections
  TempBlockIPList: TGList; //Temporary ban IP list of connections
  CurrIPaddrList: TGList;
  AttackIPaddrList: TGList; //Provisional list of IP attacks

  nMaxConnOfIPaddr: Integer = 50;
  nMaxClientPacketSize: Integer = 8000;
  nNomClientPacketSize: Integer = 200;
  dwClientCheckTimeOut: LongWord = 50; {3000}
  nMaxOverNomSizeCount: Integer = 2;
  nMaxClientMsgCount: Integer = 20;
  nCheckServerFail: Integer = 0;
  dwAttackTick: LongWord = 300;
  nAttackCount: Integer = 5;
  g_dwGameCenterHandle: THandle;

  BlockMethod: TBlockIPMethod = mDisconnect;
  bokickOverPacketSize: Boolean = True;

  //  nClientSendBlockSize        :Integer = 250; //Sent to clients data packet size limit
  nClientSendBlockSize: Integer = 1000; //Sent to clients data packet size limit
  dwClientTimeOutTime: LongWord = 5000; //Client connection session timeout (no data transmission within a specified time)
  Conf: TIniFile;
  sConfigFileName: string = '.\RunGate.ini';
  nSayMsgMaxLen: Integer = 70; //Speaking characters in length
  dwSayMsgTime: LongWord = 1000; //Time between messages
  dwHitTime: LongWord = 300; //Time between attacks
  dwSessionTimeOutTime: LongWord = 60 * 60 * 1000;
  sHintMsgPreFix: string = '[Tip]';


  g_nSpeedingDataManage: Integer = 2;
  g_boSpeedingControl: Boolean = False;
  g_nSpeedingTime: Integer = 400;
  g_boShowSpeedingMsg: Boolean = True;
  g_nShowSpeedingMsg: Integer = 0;
  g_sSpeedingMsg: string = '[Tip]: Care for the game environment, do not use illegal plugins!';

  g_btRedMsgFColor: Byte = $FF; //Foreground
  g_btRedMsgBColor: Byte = $38; //Background
  g_btGreenMsgFColor: Byte = $DB; //Foreground
  g_btGreenMsgBColor: Byte = $FF; //Background
  g_btBlueMsgFColor: Byte = $FF; //Foreground
  g_btBlueMsgBColor: Byte = $FC; //Background


  boMinimize: Boolean = True;


implementation

procedure AddMainLogMsg(Msg: string; nLevel: Integer);
var
  tMsg: string;
begin
  try
    CS_MainLog.Enter;
    if nLevel <= nShowLogLevel then begin
      tMsg := '[' + TimeToStr(Now) + '] ' + Msg;
      MainLogMsgList.Add(tMsg);
    end;
  finally
    CS_MainLog.Leave;
  end;
end;
procedure LoadAbuseFile();
var
  sFileName: string;
begin
  AddMainLogMsg('Loading abuse file...',4);
  sFileName := '.\WordFilter.txt';
  if FileExists(sFileName) then begin
    try
      CS_FilterMsg.Enter;
      AbuseList.LoadFromFile(sFileName);
    finally
      CS_FilterMsg.Leave;
    end;
  end;
  AddMainLogMsg('Abuse file loaded...',4);
end;

procedure LoadBlockIPFile();
var
  I: Integer;
  sFileName: string;
  LoadList: TStringList;
  sIPaddr: string;
  nIPaddr: Integer;
  IPaddr: pTSockaddr;
begin
  AddMainLogMsg('Loading blocked IP file...',4);
  sFileName := '.\BlockIPList.txt';
  if FileExists(sFileName) then begin
    LoadList := TStringList.Create;
    LoadList.LoadFromFile(sFileName);
    for I := 0 to LoadList.Count - 1 do begin
      sIPaddr := Trim(LoadList.Strings[I]);
      if sIPaddr = '' then Continue;
      nIPaddr := inet_addr(PChar(sIPaddr));
      if nIPaddr = INADDR_NONE then Continue;
      New(IPaddr);
      FillChar(IPaddr^, SizeOf(TSockaddr), 0);
      IPaddr.nIPaddr := nIPaddr;
      BlockIPList.Add(IPaddr);
    end;
    LoadList.Free;
  end;
  AddMainLogMsg('Blocked IP file loaded...',4);
end;

procedure SaveBlockIPList();
var
  I: Integer;
  SaveList: TStringList;
begin
  SaveList := TStringList.Create;
  for I := 0 to BlockIPList.Count - 1 do begin
    SaveList.Add(StrPas(inet_ntoa(TInAddr(pTSockaddr(BlockIPList.Items[I]).nIPaddr))));
  end;
  SaveList.SaveToFile('.\BlockIPList.txt');
  SaveList.Free;
end;

procedure SendGameCenterMsg(wIdent: Word; sSendMsg: string);
var
  SendData: TCopyDataStruct;
  nParam: Integer;
begin
  nParam := MakeLong(Word(tRunGate), wIdent);
  SendData.cbData := Length(sSendMsg) + 1;
  GetMem(SendData.lpData, SendData.cbData);
  StrCopy(SendData.lpData, PChar(sSendMsg));
  SendMessage(g_dwGameCenterHandle, WM_COPYDATA, nParam, Cardinal(@SendData));
  FreeMem(SendData.lpData);
end;

constructor TGList.Create;
begin
  inherited Create;
  InitializeCriticalSection(GLock);
end;

destructor TGList.Destroy;
begin
  DeleteCriticalSection(GLock);
  inherited;
end;

procedure TGList.Lock;
begin
  EnterCriticalSection(GLock);
end;

procedure TGList.UnLock;
begin
  LeaveCriticalSection(GLock);
end;

initialization
  begin
    Conf := TIniFile.Create(sConfigFileName);
    nShowLogLevel := Conf.ReadInteger(GateClass, 'ShowLogLevel', nShowLogLevel);
    CS_MainLog := TCriticalSection.Create;
    CS_FilterMsg := TCriticalSection.Create;
    MainLogMsgList := TStringList.Create;
    AbuseList := TStringList.Create;
    ReviceMsgList := TList.Create;
    SendMsgList := TList.Create;
    List_45AA58 := TList.Create;
    //TestList := TStringList.Create;
    boShowSckData := False;
  end;

finalization
  begin
    List_45AA58.Free;
    ReviceMsgList.Free;
    SendMsgList.Free;
    AbuseList.Free;
    MainLogMsgList.Free;
    CS_MainLog.Free;
    CS_FilterMsg.Free;
    Conf.Free;
  end;

end.

