程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> DELPHI版傳奇引擎學習菜鳥篇(applem2)-02,applem2-02

DELPHI版傳奇引擎學習菜鳥篇(applem2)-02,applem2-02

編輯:Delphi

DELPHI版傳奇引擎學習菜鳥篇(applem2)-02,applem2-02


每天只學習一個知識點,也是一種進步.

接著學習GShare.pas的實現部分,其實這個應該叫做GAMECENTER共享單元,我剛開始理解的是錯誤的,這是根據名字起的.

在學習實現部分之前,聲明部分還有一些變量:

//雖然光看這些變量不可能全部知道代表什麼,但是為了學習,還是注釋一下
var
  //下面4個應該是更新數據(格式)用的,默認為本機更新
  g_sDataListAddrs: string = '127.0.0.1';
  g_wDataListPort: Word = 18888;
  g_sDataListPassWord: string = '123456';
  g_boGetDataListOK: Boolean = False;
  //下面3個是獲取更新列表用的,
  g_DataListReadBuffer: PChar;
  g_nDataListReadLength: Integer;
  g_GetDatList: TStringList;

  //下面6個很明顯,從字面就能看出意思對應啟動設置\窗口\按鈕狀態
  g_nFormIdx: Integer;
  g_IniConf: Tinifile;
  g_sButtonStartGame: string = '啟動游戲服務器(&S)';
  g_sButtonStopGame: string = '停止游戲服務器(&T)';
  g_sButtonStopStartGame: string = '中止啟動游戲服務器(&T)';
  g_sButtonStopStopGame: string = '中止停止游戲服務器(&T)';
  //下面對應的都是配置變量,主窗口都有對應的控件
  g_sConfFile: string = '.\Config.ini';
  g_sBackListFile: string = '.\BackupList.txt';
  g_sGameName: string = '測試引擎';
  g_sGameDirectory: string = '.\';
  g_sHeroDBName: string = 'HeroDB';
  g_sExtIPaddr: string = '127.0.0.1';
  g_sExtIPaddr2: string = '127.0.0.1';
  g_boAutoRunBak: Boolean = False;
  g_boCloseWuXin: Boolean = False;
  g_boIP2: Boolean = False;
  // 聲明並初始化服務端配置結構,也許類似這樣的應該定義為類,前邊也好多
  g_Config: TConfig = (
      DBServer: (
        MainFormX: 0;
        MainFormY: 373;
        GatePort: 5100;
        ServerPort: 6000;
        GetStart: True;
        ProgramFile: 'DBServer.exe';
      );
      LoginSrv: (
        MainFormX: 252;
        MainFormY: 0;
        GatePort: 5500;
        ServerPort: 5600;
        MonPort: 3000;
        GetStart: True;
        ProgramFile: 'LoginSrv.exe';
      );
      M2Server:(
        MainFormX: 561;
        MainFormY: 0;
        GatePort: 5000;
        MsgSrvPort: 4900;
        GetStart: True;
        ProgramFile: 'M2Server.exe';
        ProgramFile: 'PlugTop.exe';
      );
      LogServer:(
        MainFormX: 252;
        MainFormY: 286;
        Port: 10000;
        GetStart: True;
        ProgramFile: 'LogDataServer.exe';
      );

       RunGate:(
        MainFormX: 437;
        MainFormY: 373;
        GetStart: (True, False, False, False, False, False, False, False);
        GatePort: (7200, 7201, 7202, 7203, 7204, 7205, 7206, 7207);
        ProgramFile: 'RunGate.exe';
      );
      SelGate:(
        MainFormX: 0;
        MainFormY: 180;
        GatePort: (7100, 7101);
        GetStart1: True;
        GetStart2: False;
        ProgramFile: 'SelGate.exe';
      );
      LoginGate:(
        MainFormX: 0;
        MainFormY: 0;
        GatePort: 7000;
        GetStart: True;
        ProgramFile: 'LoginGate.exe';
      );
       PlugTop:(
        MainFormX: 525;
        MainFormY:300;
        GetStart: True;
        ProgramFile: 'PlugTop.exe';
      );
    );
  //聲明服務程序變量,暫時就這麼理解吧
  DBServer: TProgram;
  LoginServer: TProgram;
  LogServer: TProgram;
  M2Server: TProgram;
  RunGate: array[0..MAXRUNGATECOUNT - 1] of TProgram;
  SelGate: TProgram;
  SelGate1: TProgram;
  LoginGate: TProgram;
  LoginGate2: TProgram;
  PlugTop: TProgram;
  //下面是檢測程序運行狀態的變量,暫時理解為心跳檢測吧
  g_dwStopTick: LongWord;
  g_dwStopTimeOut: LongWord = 10000;
  g_dwM2CheckCodeAddr: LongWord;
  g_dwDBCheckCodeAddr: LongWord;
  g_BackUpManager: TBackUpManager;
  m_nBackStartStatus: Integer = 0;

到此GShare.pas的聲明部分結束,覺得還可以優化,接下來是實現部分,實現一共有5個函數\過程:

procedure LoadConfig();//加載配置
procedure SaveConfig();//保存配置
{運行服務}
function RunProgram(var ProgramInfo: TProgram; sHandle: string; dwWaitTime: LongWord): LongWord;
{停止服務}
function StopProgram(var ProgramInfo: TProgram; dwWaitTime: LongWord): Integer;
{發送消息}
procedure SendProgramMsg(DesForm: THandle; wIdent: Word; sSendMsg: string);

加載保存配置就是讀取和寫入服務端裡邊的INI和TXT配置文件,啟動停止服務發送數據(消息)用的是API:

//讀取配置信息
procedure LoadConfig();
begin
  //下面一堆都是讀取INI文件,准備後續優化一下
  g_sGameDirectory := g_IniConf.ReadString(BasicSectionName, 'GameDirectory', g_sGameDirectory);
  g_sHeroDBName := g_IniConf.ReadString(BasicSectionName, 'HeroDBName', g_sHeroDBName);
  g_sGameName := g_IniConf.ReadString(BasicSectionName, 'GameName', g_sGameName);
  g_sExtIPaddr := g_IniConf.ReadString(BasicSectionName, 'ExtIPaddr', g_sExtIPaddr);
  g_sExtIPaddr2 := g_IniConf.ReadString(BasicSectionName, 'ExtIPaddr2', g_sExtIPaddr2);
  g_boAutoRunBak := g_IniConf.ReadBool(BasicSectionName, 'AutoRunBak', g_boAutoRunBak);
  g_boIP2 := g_IniConf.ReadBool(BasicSectionName, 'IP2', g_boIP2);
  g_boCloseWuXin := g_IniConf.ReadBool(BasicSectionName, 'CloseWuXin', g_boCloseWuXin);

  g_Config.DBServer.MainFormX := g_IniConf.ReadInteger(DBServerSectionName, 'MainFormX', g_Config.DBServer.MainFormX);
  g_Config.DBServer.MainFormY := g_IniConf.ReadInteger(DBServerSectionName, 'MainFormY', g_Config.DBServer.MainFormY);
  g_Config.DBServer.GatePort := g_IniConf.ReadInteger(DBServerSectionName, 'GatePort', g_Config.DBServer.GatePort);
  g_Config.DBServer.ServerPort := g_IniConf.ReadInteger(DBServerSectionName, 'ServerPort', g_Config.DBServer.ServerPort);
  g_Config.DBServer.GetStart := g_IniConf.ReadBool(DBServerSectionName, 'GetStart', g_Config.DBServer.GetStart);

  g_Config.LoginSrv.MainFormX := g_IniConf.ReadInteger(LoginSrvSectionName, 'MainFormX', g_Config.LoginSrv.MainFormX);
  g_Config.LoginSrv.MainFormY := g_IniConf.ReadInteger(LoginSrvSectionName, 'MainFormY', g_Config.LoginSrv.MainFormY);
  g_Config.LoginSrv.GatePort := g_IniConf.ReadInteger(LoginSrvSectionName, 'GatePort', g_Config.LoginSrv.GatePort);
  g_Config.LoginSrv.ServerPort := g_IniConf.ReadInteger(LoginSrvSectionName, 'ServerPort', g_Config.LoginSrv.ServerPort);
  g_Config.LoginSrv.MonPort := g_IniConf.ReadInteger(LoginSrvSectionName, 'MonPort', g_Config.LoginSrv.MonPort);
  g_Config.LoginSrv.GetStart := g_IniConf.ReadBool(LoginSrvSectionName, 'GetStart', g_Config.LoginSrv.GetStart);

  g_Config.M2Server.MainFormX := g_IniConf.ReadInteger(M2ServerSectionName, 'MainFormX', g_Config.M2Server.MainFormX);
  g_Config.M2Server.MainFormY := g_IniConf.ReadInteger(M2ServerSectionName, 'MainFormY', g_Config.M2Server.MainFormY);
  g_Config.M2Server.GatePort := g_IniConf.ReadInteger(M2ServerSectionName, 'GatePort', g_Config.M2Server.GatePort);
  g_Config.M2Server.MsgSrvPort := g_IniConf.ReadInteger(M2ServerSectionName, 'MsgSrvPort', g_Config.M2Server.MsgSrvPort);
  g_Config.M2Server.GetStart := g_IniConf.ReadBool(M2ServerSectionName, 'GetStart', g_Config.M2Server.GetStart);

  g_Config.LogServer.MainFormX := g_IniConf.ReadInteger(LogServerSectionName, 'MainFormX', g_Config.LogServer.MainFormX);
  g_Config.LogServer.MainFormY := g_IniConf.ReadInteger(LogServerSectionName, 'MainFormY', g_Config.LogServer.MainFormY);
  g_Config.LogServer.Port := g_IniConf.ReadInteger(LogServerSectionName, 'Port', g_Config.LogServer.Port);
  g_Config.LogServer.GetStart := g_IniConf.ReadBool(LogServerSectionName, 'GetStart', g_Config.LogServer.GetStart);

  g_Config.RunGate.MainFormX := g_IniConf.ReadInteger(RunGateSectionName, 'MainFormX', g_Config.RunGate.MainFormX);
  g_Config.RunGate.MainFormY := g_IniConf.ReadInteger(RunGateSectionName, 'MainFormY', g_Config.RunGate.MainFormY);
  g_Config.RunGate.GetStart[0] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart1', g_Config.RunGate.GetStart[0]);
  g_Config.RunGate.GetStart[1] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart2', g_Config.RunGate.GetStart[1]);
  g_Config.RunGate.GetStart[2] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart3', g_Config.RunGate.GetStart[2]);
  g_Config.RunGate.GetStart[3] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart4', g_Config.RunGate.GetStart[3]);
  g_Config.RunGate.GetStart[4] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart5', g_Config.RunGate.GetStart[4]);
  g_Config.RunGate.GetStart[5] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart6', g_Config.RunGate.GetStart[5]);
  g_Config.RunGate.GetStart[6] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart7', g_Config.RunGate.GetStart[6]);
  g_Config.RunGate.GetStart[7] := g_IniConf.ReadBool(RunGateSectionName, 'GetStart8', g_Config.RunGate.GetStart[7]);

  g_Config.RunGate.GatePort[0] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort1', g_Config.RunGate.GatePort[0]);
  g_Config.RunGate.GatePort[1] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort2', g_Config.RunGate.GatePort[1]);
  g_Config.RunGate.GatePort[2] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort3', g_Config.RunGate.GatePort[2]);
  g_Config.RunGate.GatePort[3] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort4', g_Config.RunGate.GatePort[3]);
  g_Config.RunGate.GatePort[4] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort5', g_Config.RunGate.GatePort[4]);
  g_Config.RunGate.GatePort[5] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort6', g_Config.RunGate.GatePort[5]);
  g_Config.RunGate.GatePort[6] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort7', g_Config.RunGate.GatePort[6]);
  g_Config.RunGate.GatePort[7] := g_IniConf.ReadInteger(RunGateSectionName, 'GatePort8', g_Config.RunGate.GatePort[7]);

  g_Config.SelGate.MainFormX := g_IniConf.ReadInteger(SelGateSectionName, 'MainFormX', g_Config.SelGate.MainFormX);
  g_Config.SelGate.MainFormY := g_IniConf.ReadInteger(SelGateSectionName, 'MainFormY', g_Config.SelGate.MainFormY);
  g_Config.SelGate.GatePort[0] := g_IniConf.ReadInteger(SelGateSectionName, 'GatePort1', g_Config.SelGate.GatePort[0]);
  g_Config.SelGate.GatePort[1] := g_IniConf.ReadInteger(SelGateSectionName, 'GatePort2', g_Config.SelGate.GatePort[1]);
  g_Config.SelGate.GetStart1 := g_IniConf.ReadBool(SelGateSectionName, 'GetStart1', g_Config.SelGate.GetStart1);
  g_Config.SelGate.GetStart2 := g_IniConf.ReadBool(SelGateSectionName, 'GetStart2', g_Config.SelGate.GetStart2);

  g_Config.LoginGate.MainFormX := g_IniConf.ReadInteger(LoginGateSectionName, 'MainFormX', g_Config.LoginGate.MainFormX);
  g_Config.LoginGate.MainFormY := g_IniConf.ReadInteger(LoginGateSectionName, 'MainFormY', g_Config.LoginGate.MainFormY);
  g_Config.LoginGate.GatePort := g_IniConf.ReadInteger(LoginGateSectionName, 'GatePort', g_Config.LoginGate.GatePort);
  g_Config.LoginGate.GetStart := g_IniConf.ReadBool(LoginGateSectionName, 'GetStart', g_Config.LoginGate.GetStart);

  g_Config.PlugTop.MainFormX := g_IniConf.ReadInteger(PlugTopSectionName, 'MainFormX', g_Config.PlugTop.MainFormX);
  g_Config.PlugTop.MainFormY := g_IniConf.ReadInteger(PlugTopSectionName, 'MainFormY', g_Config.PlugTop.MainFormY);
  g_Config.PlugTop.GetStart := g_IniConf.ReadBool(PlugTopSectionName, 'GetStart', g_Config.PlugTop.GetStart);
end;

procedure SaveConfig();   //保存配置信息
begin
  //沒什麼可注釋的,寫入INI
  g_IniConf.WriteString(BasicSectionName, 'GameDirectory', g_sGameDirectory);
  g_IniConf.WriteString(BasicSectionName, 'HeroDBName', g_sHeroDBName);
  g_IniConf.WriteString(BasicSectionName, 'GameName', g_sGameName);
  g_IniConf.WriteString(BasicSectionName, 'ExtIPaddr', g_sExtIPaddr);
  g_IniConf.WriteString(BasicSectionName, 'ExtIPaddr2', g_sExtIPaddr2);
  g_IniConf.WriteBool(BasicSectionName, 'AutoRunBak', g_boAutoRunBak);
  g_IniConf.WriteBool(BasicSectionName, 'IP2', g_boIP2);
  g_IniConf.WriteBool(BasicSectionName, 'CloseWuXin', g_boCloseWuXin);

  g_IniConf.WriteInteger(DBServerSectionName, 'MainFormX', g_Config.DBServer.MainFormX);
  g_IniConf.WriteInteger(DBServerSectionName, 'MainFormY', g_Config.DBServer.MainFormY);
  g_IniConf.WriteInteger(DBServerSectionName, 'GatePort', g_Config.DBServer.GatePort);
  g_IniConf.WriteInteger(DBServerSectionName, 'ServerPort', g_Config.DBServer.ServerPort);
  g_IniConf.WriteBool(DBServerSectionName, 'GetStart', g_Config.DBServer.GetStart);

  g_IniConf.WriteInteger(LoginSrvSectionName, 'MainFormX', g_Config.LoginSrv.MainFormX);
  g_IniConf.WriteInteger(LoginSrvSectionName, 'MainFormY', g_Config.LoginSrv.MainFormY);
  g_IniConf.WriteInteger(LoginSrvSectionName, 'GatePort', g_Config.LoginSrv.GatePort);
  g_IniConf.WriteInteger(LoginSrvSectionName, 'ServerPort', g_Config.LoginSrv.ServerPort);
  g_IniConf.WriteInteger(LoginSrvSectionName, 'MonPort', g_Config.LoginSrv.MonPort);
  g_IniConf.WriteBool(LoginSrvSectionName, 'GetStart', g_Config.LoginSrv.GetStart);

  g_IniConf.WriteInteger(M2ServerSectionName, 'MainFormX', g_Config.M2Server.MainFormX);
  g_IniConf.WriteInteger(M2ServerSectionName, 'MainFormY', g_Config.M2Server.MainFormY);
  g_IniConf.WriteInteger(M2ServerSectionName, 'GatePort', g_Config.M2Server.GatePort);
  g_IniConf.WriteInteger(M2ServerSectionName, 'MsgSrvPort', g_Config.M2Server.MsgSrvPort);
  g_IniConf.WriteBool(M2ServerSectionName, 'GetStart', g_Config.M2Server.GetStart);

  g_IniConf.WriteInteger(LogServerSectionName, 'MainFormX', g_Config.LogServer.MainFormX);
  g_IniConf.WriteInteger(LogServerSectionName, 'MainFormY', g_Config.LogServer.MainFormY);
  g_IniConf.WriteInteger(LogServerSectionName, 'Port', g_Config.LogServer.Port);
  g_IniConf.WriteBool(LogServerSectionName, 'GetStart', g_Config.LogServer.GetStart);

  g_IniConf.WriteInteger(RunGateSectionName, 'MainFormX', g_Config.RunGate.MainFormX);
  g_IniConf.WriteInteger(RunGateSectionName, 'MainFormY', g_Config.RunGate.MainFormY);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart1', g_Config.RunGate.GetStart[0]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart2', g_Config.RunGate.GetStart[1]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart3', g_Config.RunGate.GetStart[2]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart4', g_Config.RunGate.GetStart[3]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart5', g_Config.RunGate.GetStart[4]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart6', g_Config.RunGate.GetStart[5]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart7', g_Config.RunGate.GetStart[6]);
  g_IniConf.WriteBool(RunGateSectionName, 'GetStart8', g_Config.RunGate.GetStart[7]);

  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort1', g_Config.RunGate.GatePort[0]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort2', g_Config.RunGate.GatePort[1]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort3', g_Config.RunGate.GatePort[2]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort4', g_Config.RunGate.GatePort[3]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort5', g_Config.RunGate.GatePort[4]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort6', g_Config.RunGate.GatePort[5]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort7', g_Config.RunGate.GatePort[6]);
  g_IniConf.WriteInteger(RunGateSectionName, 'GatePort8', g_Config.RunGate.GatePort[7]);

  g_IniConf.WriteInteger(SelGateSectionName, 'MainFormX', g_Config.SelGate.MainFormX);
  g_IniConf.WriteInteger(SelGateSectionName, 'MainFormY', g_Config.SelGate.MainFormY);
  g_IniConf.WriteInteger(SelGateSectionName, 'GatePort1', g_Config.SelGate.GatePort[0]);
  g_IniConf.WriteInteger(SelGateSectionName, 'GatePort2', g_Config.SelGate.GatePort[1]);
  g_IniConf.WriteBool(SelGateSectionName, 'GetStart1', g_Config.SelGate.GetStart1);
  g_IniConf.WriteBool(SelGateSectionName, 'GetStart2', g_Config.SelGate.GetStart2);

  g_IniConf.WriteInteger(LoginGateSectionName, 'MainFormX', g_Config.LoginGate.MainFormX);
  g_IniConf.WriteInteger(LoginGateSectionName, 'MainFormY', g_Config.LoginGate.MainFormY);
  g_IniConf.WriteInteger(LoginGateSectionName, 'GatePort', g_Config.LoginGate.GatePort);
  g_IniConf.WriteBool(LoginGateSectionName, 'GetStart', g_Config.LoginGate.GetStart);

  g_IniConf.WriteInteger(PlugTopSectionName, 'MainFormX', g_Config.PlugTop.MainFormX);
  g_IniConf.WriteInteger(PlugTopSectionName, 'MainFormY', g_Config.PlugTop.MainFormY);
  g_IniConf.WriteBool(PlugTopSectionName, 'GetStart', g_Config.PlugTop.GetStart);
end;
{啟動服務,用的大部分是API}
function RunProgram(var ProgramInfo: TProgram; sHandle: string; dwWaitTime: LongWord): LongWord;
var
  StartupInfo: TStartupInfo; //獲取窗口狀態
  sCommandLine: string;      //程序完整路徑
  sCurDirectory: string;     //程序目錄
begin
  Result := 0;
  FillChar(StartupInfo, SizeOf(TStartupInfo), #0); //這個雖然查看了API參考,還是不理解,暫時就認為它是初始化吧
  GetStartupInfo(StartupInfo);//獲取進程的啟動信息
  {格式化程序完整路徑字符串}
  sCommandLine := format('%s%s %s %d %d', [ProgramInfo.sDirectory, ProgramInfo.sProgramFile, sHandle, ProgramInfo.nMainFormX, ProgramInfo.nMainFormY]);
  sCurDirectory := ProgramInfo.sDirectory; //取得程序運行目錄
  if not CreateProcess(nil,  //如果啟動服務失敗返回錯誤代碼
    PChar(sCommandLine),
    nil,
    nil,
    True,
    0,
    nil,
    PChar(sCurDirectory),
    StartupInfo,
    ProgramInfo.ProcessInfo) then
  begin
    Result := GetLastError();
  end;
  Sleep(dwWaitTime);  //等待
end;
{停止服務,這個函數很簡單,不再注釋}
function StopProgram(var ProgramInfo: TProgram; dwWaitTime: LongWord): Integer;
var
  dwExitCode: LongWord;
begin
  Result := 0;
  dwExitCode := 0;
  if TerminateProcess(ProgramInfo.ProcessHandle, dwExitCode) then
  begin
    Result := GetLastError();
  end;
  Sleep(dwWaitTime);
end;
{這個是向指定程序發送數據}
procedure SendProgramMsg(DesForm: THandle; wIdent: Word; sSendMsg: string);
var
  SendData: TCopyDataStruct;
  nParam: Integer;
begin
  nParam := MakeLong(0, wIdent);
  SendData.cbData := length(sSendMsg) + 1;
  GetMem(SendData.lpData, SendData.cbData);
  StrCopy(SendData.lpData, PChar(sSendMsg));
  SendMessage(DesForm, WM_COPYDATA, nParam, Cardinal(@SendData));
  FreeMem(SendData.lpData);
end;
{初始化和反初始化}
initialization
  begin
    g_IniConf := Tinifile.Create(g_sConfFile);
  end;

finalization
  begin
    g_IniConf.Free;
  end;

主要是加載和保存配置過程過程重復的事情很多,可以考慮改進.

整個GShare.pas單元學習完畢,接下來可以開始主單元文件學習了.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved