Statistics
| Branch: | Tag: | Revision:

root / Client / UdmNetwork.pas @ 0:95bd93c28625

History | View | Annotate | Download (11.4 kB)

1
(*
2
 * CDDL HEADER START
3
 *
4
 * The contents of this file are subject to the terms of the
5
 * Common Development and Distribution License, Version 1.0 only
6
 * (the "License").  You may not use this file except in compliance
7
 * with the License.
8
 *
9
 * You can obtain a copy of the license at
10
 * http://www.opensource.org/licenses/cddl1.php.
11
 * See the License for the specific language governing permissions
12
 * and limitations under the License.
13
 *
14
 * When distributing Covered Code, include this CDDL HEADER in each
15
 * file and include the License file at
16
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
17
 * add the following below this CDDL HEADER, with the fields enclosed
18
 * by brackets "[]" replaced with your own identifying * information:
19
 *      Portions Copyright [yyyy] [name of copyright owner]
20
 *
21
 * CDDL HEADER END
22
 *
23
 *
24
 *      Portions Copyright 2007 Andreas Schneider
25
 *)
26
unit UdmNetwork;
27
28
{$mode objfpc}{$H+}
29
30
interface
31
32
uses
33
  Classes, SysUtils, LResources, Forms, Controls, Dialogs, lNetComponents, lNet,
34
  UEnhancedMemoryStream, UPacket, UEnums, ExtCtrls, dateutils;
35
36
type
37
38
  { TdmNetwork }
39
40
  TdmNetwork = class(TDataModule)
41
    TCPClient: TLTCPComponent;
42
    tmNoOp: TTimer;
43
    procedure DataModuleCreate(Sender: TObject);
44
    procedure DataModuleDestroy(Sender: TObject);
45
    procedure TCPClientConnect(aSocket: TLSocket);
46
    procedure TCPClientDisconnect(aSocket: TLSocket);
47
    procedure TCPClientError(const msg: string; aSocket: TLSocket);
48
    procedure TCPClientReceive(aSocket: TLSocket);
49
    procedure tmNoOpStartTimer(Sender: TObject);
50
    procedure tmNoOpTimer(Sender: TObject);
51
  protected
52
    FSendQueue: TEnhancedMemoryStream;
53
    FReceiveQueue: TEnhancedMemoryStream;
54
    FUsername: string;
55
    FPassword: string;
56
    FAccessLevel: TAccessLevel;
57
    FDataDir: string;
58
    FLastPacket: TDateTime;
59
    procedure OnCanSend(ASocket: TLSocket);
60
    procedure OnConnectionHandlingPacket(ABuffer: TEnhancedMemoryStream);
61
    procedure ProcessQueue;
62
    procedure DoLogin;
63
  public
64
    property Username: string read FUsername;
65
    property AccessLevel: TAccessLevel read FAccessLevel write FAccessLevel;
66
    procedure Send(APacket: TPacket);
67
    procedure Disconnect;
68
    procedure CheckClose(ASender: TForm);
69
  end; 
70
71
var
72
  dmNetwork: TdmNetwork;
73
74
implementation
75
76
uses
77
  UPacketHandlers, UPackets, UfrmMain, UfrmLogin, UfrmInitialize,
78
  UGameResources, UfrmAccountControl, UfrmEditAccount, UfrmDrawSettings,
79
  UfrmBoundaries, UfrmElevateSettings, UfrmConfirmation, UfrmMoveSettings,
80
  UfrmAbout, UfrmHueSettings, UfrmRadar, UfrmLargeScaleCommand,
81
  UfrmVirtualLayer, UfrmFilter, UfrmTileInfo;
82
  
83
{$I version.inc}
84
85
{ TdmNetwork }
86
87
procedure TdmNetwork.DataModuleCreate(Sender: TObject);
88
begin
89
  FSendQueue := TEnhancedMemoryStream.Create;
90
  FReceiveQueue := TEnhancedMemoryStream.Create;
91
  TCPClient.OnCanSend := @OnCanSend;
92
  PacketHandlers[$02] := TPacketHandler.Create(0, @OnConnectionHandlingPacket);
93
  DoLogin;
94
end;
95
96
procedure TdmNetwork.DataModuleDestroy(Sender: TObject);
97
begin
98
  if FSendQueue <> nil then FreeAndNil(FSendQueue);
99
  if FReceiveQueue <> nil then FreeAndNil(FReceiveQueue);
100
  if PacketHandlers[$02] <> nil then FreeAndNil(PacketHandlers[$02]);
101
end;
102
103
procedure TdmNetwork.TCPClientConnect(aSocket: TLSocket);
104
begin
105
  FSendQueue.Clear;
106
  FReceiveQueue.Clear;
107
end;
108
109
procedure TdmNetwork.TCPClientDisconnect(aSocket: TLSocket);
110
begin
111
  FSendQueue.Clear;
112
  FReceiveQueue.Clear;
113
  DoLogin;
114
end;
115
116
procedure TdmNetwork.TCPClientError(const msg: string; aSocket: TLSocket);
117
begin
118
  MessageDlg('Connection error', msg, mtError, [mbOK], 0);
119
  if not TCPClient.Connected then
120
    TCPClientDisconnect(aSocket);
121
end;
122
123
procedure TdmNetwork.TCPClientReceive(aSocket: TLSocket);
124
var
125
  buffer: array[0..4095] of byte;
126
  size: Integer;
127
begin
128
  repeat
129
    size := TCPClient.Get(buffer, 4096);
130
    if size > 0 then
131
      FReceiveQueue.Enqueue(buffer, size);
132
  until size <= 0;
133
  ProcessQueue;
134
end;
135
136
procedure TdmNetwork.tmNoOpStartTimer(Sender: TObject);
137
begin
138
  FLastPacket := Now;
139
end;
140
141
procedure TdmNetwork.tmNoOpTimer(Sender: TObject);
142
begin
143
  if SecondsBetween(FLastPacket, Now) > 25 then
144
    Send(TNoOpPacket.Create);
145
end;
146
147
procedure TdmNetwork.OnCanSend(ASocket: TLSocket);
148
var
149
  size: Integer;
150
begin
151
  while FSendQueue.Size > 0 do
152
  begin
153
    FLastPacket := Now;
154
    size := TCPClient.Send(FSendQueue.Memory^, FSendQueue.Size);
155
    if size > 0 then
156
      FSendQueue.Dequeue(size)
157
    else
158
      Break;
159
  end;
160
end;
161
162
procedure TdmNetwork.OnConnectionHandlingPacket(ABuffer: TEnhancedMemoryStream);
163
var
164
  subID: Byte;
165
  loginState: TLoginState;
166
  width, height: Word;
167
  serverState: TServerState;
168
begin
169
  subID := ABuffer.ReadByte;
170
  case subID of
171
    $01:
172
      begin
173
        if ABuffer.ReadCardinal = ProtocolVersion then
174
        begin
175
          frmInitialize.lblStatus.Caption := 'Authenticating';
176
          Send(TLoginRequestPacket.Create(FUsername, FPassword));
177
        end else
178
        begin
179
          MessageDlg('Error', 'Invalid protocol version. Maybe your client is outdated.', mtError, [mbOK], 0);
180
          Disconnect;
181
        end;
182
      end;
183
    $03:
184
      begin
185
        loginState := TLoginState(ABuffer.ReadByte);
186
        if loginState = lsOK then
187
        begin
188
          frmInitialize.lblStatus.Caption := 'Initializing';
189
          frmInitialize.Repaint;
190
          frmInitialize.lblStatus.Repaint;
191
          Application.ProcessMessages;
192
          FAccessLevel := TAccessLevel(ABuffer.ReadByte);
193
          InitGameResourceManager(FDataDir);
194
          width := ABuffer.ReadWord;
195
          height := ABuffer.ReadWord;
196
          ResMan.InitLandscape(width, height);
197
          frmMain := TfrmMain.Create(dmNetwork);
198
          frmAccountControl := TfrmAccountControl.Create(frmMain);
199
          frmEditAccount := TfrmEditAccount.Create(frmAccountControl);
200
          frmConfirmation := TfrmConfirmation.Create(frmMain);
201
          frmDrawSettings := TfrmDrawSettings.Create(frmMain);
202
          frmMoveSettings := TfrmMoveSettings.Create(frmMain);
203
          frmElevateSettings := TfrmElevateSettings.Create(frmMain);
204
          frmHueSettings := TfrmHueSettings.Create(frmMain);
205
          frmBoundaries := TfrmBoundaries.Create(frmMain);
206
          frmFilter := TfrmFilter.Create(frmMain);
207
          frmVirtualLayer := TfrmVirtualLayer.Create(frmMain);
208
          frmAbout := TfrmAbout.Create(frmMain);
209
          frmRadarMap := TfrmRadarMap.Create(frmMain);
210
          frmLargeScaleCommand := TfrmLargeScaleCommand.Create(frmMain);
211
          frmTileInfo := TfrmTileInfo.Create(frmMain);
212
          frmMain.Show;
213
          frmInitialize.Hide;
214
          tmNoOp.Enabled := True;
215
        end else
216
        begin
217
          if loginState = lsInvalidUser then
218
            MessageDlg('Error', 'The username you specified is incorrect.', mtWarning, [mbOK], 0)
219
          else if loginState = lsInvalidPassword then
220
            MessageDlg('Error', 'The password you specified is incorrect.', mtWarning, [mbOK], 0)
221
          else if loginState = lsAlreadyLoggedIn then
222
            MessageDlg('Error', 'There is already a client logged in using that account.', mtWarning, [mbOK], 0)
223
          else if loginState = lsNoAccess then
224
            MessageDlg('Error', 'This account has no access.', mtWarning, [mbOK], 0);
225
        end;
226
      end;
227
    $04: //Server state
228
      begin
229
        serverState := TServerState(ABuffer.ReadByte);
230
        if serverState = ssRunning then
231
        begin
232
          frmInitialize.UnsetModal;
233
          frmInitialize.Hide;
234
          tmNoOp.Enabled := True;
235
        end else
236
        begin
237
          case serverState of
238
            ssFrozen: frmInitialize.lblStatus.Caption := 'The server is currently paused.';
239
            ssOther: frmInitialize.lblStatus.Caption := ABuffer.ReadStringNull
240
          end;
241
          tmNoOp.Enabled := False;
242
          frmInitialize.Show;
243
          frmInitialize.SetModal;
244
        end;
245
      end;
246
  end;
247
end;
248
249
procedure TdmNetwork.ProcessQueue;
250
var
251
  packetHandler: TPacketHandler;
252
  size: Cardinal;
253
begin
254
  FReceiveQueue.Position := 0;
255
  while FReceiveQueue.Size >= 1 do
256
  begin
257
    packetHandler := PacketHandlers[FReceiveQueue.ReadByte];
258
    if packetHandler <> nil then
259
    begin
260
      size := packetHandler.PacketLength;
261
      if size = 0 then
262
      begin
263
        if FReceiveQueue.Size > 5 then
264
          size := FReceiveQueue.ReadCardinal
265
        else
266
          Break; //wait for more data
267
      end;
268
269
      if FReceiveQueue.Size >= size then
270
      begin
271
        FReceiveQueue.Lock(FReceiveQueue.Position, size - FReceiveQueue.Position); //prevent handler from reading too much
272
        packetHandler.Process(FReceiveQueue);
273
        FReceiveQueue.Unlock;
274
        FReceiveQueue.Dequeue(size);
275
      end else
276
        Break; //wait for more data
277
    end else
278
    begin
279
      {Writeln('Dropping client due to unknown packet: ', ANetState.Socket.PeerAddress);}
280
      Disconnect;
281
      FReceiveQueue.Clear;
282
    end;
283
  end;
284
end;
285
286
procedure TdmNetwork.DoLogin;
287
begin
288
  tmNoOp.Enabled := False;
289
  frmLogin := TfrmLogin.Create(dmNetwork);
290
  if frmInitialize = nil then frmInitialize := TfrmInitialize.Create(dmNetwork);
291
  if frmTileInfo <> nil then FreeAndNil(frmTileInfo);
292
  if frmLargeScaleCommand <> nil then FreeAndNil(frmLargeScaleCommand);
293
  if frmEditAccount <> nil then FreeAndNil(frmEditAccount);
294
  if frmAccountControl <> nil then FreeAndNil(frmAccountControl);
295
  if frmConfirmation <> nil then FreeAndNil(frmConfirmation);
296
  if frmDrawSettings <> nil then FreeAndNil(frmDrawSettings);
297
  if frmMoveSettings <> nil then FreeAndNil(frmMoveSettings);
298
  if frmElevateSettings <> nil then FreeAndNil(frmElevateSettings);
299
  if frmHueSettings <> nil then FreeAndNil(frmHueSettings);
300
  if frmBoundaries <> nil then FreeAndNil(frmBoundaries);
301
  if frmFilter <> nil then FreeAndNil(frmFilter);
302
  if frmVirtualLayer <> nil then FreeAndNil(frmVirtualLayer);
303
  if frmAbout <> nil then FreeAndNil(frmAbout);
304
  if frmRadarMap <> nil then FreeAndNil(frmRadarMap);
305
  if frmMain <> nil then
306
  begin
307
    frmMain.ApplicationProperties1.OnIdle := nil;
308
    FreeAndNil(frmMain);
309
  end;
310
  if GameResourceManager <> nil then FreeAndNil(GameResourceManager);
311
  frmInitialize.Hide;
312
  while frmLogin.ShowModal = mrOK do
313
  begin
314
    if TCPClient.Connect(frmLogin.edHost.Text, frmLogin.edPort.Value) then
315
    begin
316
      FUsername := frmLogin.edUsername.Text;
317
      FPassword := frmLogin.edPassword.Text;
318
      FDataDir := frmLogin.edData.Text;
319
      frmInitialize.lblStatus.Caption := 'Connecting';
320
      frmInitialize.Show;
321
      Break;
322
    end else
323
      MessageDlg('Error', 'Cannot connect to the specified server.', mtError, [mbOK], 0);
324
  end;
325
  frmLogin.Close;
326
  FreeAndNil(frmLogin);
327
end;
328
329
procedure TdmNetwork.Send(APacket: TPacket);
330
var
331
  source: TEnhancedMemoryStream;
332
begin
333
  if TCPClient.Connected then
334
  begin
335
    FSendQueue.Seek(0, soFromEnd);
336
    source := APacket.Stream;
337
    FSendQueue.CopyFrom(source, 0);
338
    OnCanSend(nil);
339
  end;
340
  APacket.Free;
341
end;
342
343
procedure TdmNetwork.Disconnect;
344
begin
345
  Send(TQuitPacket.Create);
346
end;
347
348
procedure TdmNetwork.CheckClose(ASender: TForm);
349
begin
350
  if ((frmLogin = nil) or (ASender = frmLogin)) and
351
     ((frmMain = nil) or (ASender = frmMain)) and
352
     ((frmInitialize = nil) or (not frmInitialize.Visible)) then
353
  begin
354
    Application.Terminate;
355
  end;
356
end;
357
358
initialization
359
  {$I UdmNetwork.lrs}
360
361
end.
362