1{$MODE OBJFPC}
2{$LONGSTRINGS ON}
3
4unit UENetClass;
5
6{
7  ENet UDP Class for Free Pascal
8
9  Copyright (c) 2013 Do-wan Kim
10  2016-08-24: revised by Dmitry D. Chernov
11
12  Permission is hereby granted, free of charge, to any person obtaining a
13  copy of this software and associated documentation files (the "Software"),
14  to deal in the Software without restriction, including without limitation
15  the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  and/or sell copies of the Software, and to permit persons to whom the
17  Software is furnished to do so, subject to the following conditions:
18
19  The above copyright notice and this permission notice shall be included in
20  all copies or substantial portions of the Software.
21
22  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  DEALINGS IN THE SOFTWARE.
29
30  TODO:
31  - Add SendMsgEventPeer
32}
33
34interface
35
36uses
37  SysUtils, Classes, ENet;
38
39type
40  TENetEventType = ( ENetEventNone, ENetEventConnect, ENetEventDisconnect,
41    ENetEventReceive );
42
43  TENetPacketFlag = ( ENetPacketReliable, ENetPacketUnsequenced,
44    ENetPacketNoAllocate, ENetPacketUnReliableFragment );
45  TENetPacketFlags = set of TENetPacketFlag;
46
47  TENetEventProc = procedure( const Event: ENetEvent ) of object;
48  TENetEventRecv = procedure( const Event: ENetEvent; var BroadcastMsg: Boolean;
49    var BroadcastChannel: Byte ) of object;
50
51  { TENetClass }
52
53  TENetClass = class
54    private
55      FInit : Boolean;
56
57      FHostname : string;
58      FAddress : ENetAddress;
59      FIsServer : Boolean;
60
61      FMaxPeer : Cardinal;
62      FMaxChannels : Byte;
63      FBandwidthIncoming : Cardinal;
64      FBandwidthOutgoing : Cardinal;
65
66      FHost : pENetHost;
67      FEvent : ENetEvent;
68      FPeer : pENetPeer;
69
70      FClientData : Cardinal;
71      FConnectTimeout : Cardinal;
72      FMessageTimeout : Cardinal;
73
74      FEventNone : TENetEventProc;
75      FEventConnect : TENetEventProc;
76      FEventDisconnect : TENetEventProc;
77      FEventReceive : TENetEventRecv;
78
79    public
80      constructor Create( Port: Word; bServer: Boolean );
81      destructor Destroy(); override;
82
83      function InitHost(): Boolean;
84      procedure DeinitHost();
85      function Connect( const Host: string; Port: Word ): Boolean;
86      function Disconnect( bNow: Boolean ): Boolean;
87      function SendMsg( Channel: Byte; Data: Pointer; Length: Integer;
88        flag: TENetPacketFlags; WaitResponse: Boolean = False ): Boolean;
89      function SendMsgEventPeer( Data: Pointer; Length: Integer;
90        flag: TENetPacketFlags; WaitResponse: Boolean = False ): Boolean;
91      procedure FlushMsg();
92      function BroadcastMsg( Channel: Byte; Data: Pointer; Length: Integer;
93        flag: TENetPacketFlags; WaitResponse: Boolean = False ): Boolean;
94      function ProcessMsg(): Boolean; virtual;
95      procedure Ping();
96
97
98      property Port: Word read FAddress.port write FAddress.port;
99      property MaxClient: Cardinal read FMaxPeer write FMaxPeer;
100      property MaxChannels: Byte read FMaxChannels write FMaxChannels;
101      property BandwidthIncoming: Cardinal read FBandwidthIncoming write FBandwidthIncoming;
102      property BandwidthOutgoing: Cardinal read FBandwidthOutgoing write FBandwidthOutgoing;
103      property ClientData: Cardinal read FClientData;
104      property ConnectTimeout: Cardinal read FConnectTimeout write FConnectTimeout;
105      property MessageTimeout: Cardinal read FMessageTimeout write FMessageTimeout;
106      property OnNone: TENetEventProc read FEventNone write FEventNone;
107      property OnConnect: TENetEventProc read FEventConnect write FEventConnect;
108      property OnDisconnect: TENetEventProc read FEventDisconnect write FEventDisconnect;
109      property OnReceive: TENetEventRecv read FEventReceive write FEventReceive;
110  end;
111
112implementation
113
114function GetPacketFlag( flag: TENetPacketFlags ): Integer;
115begin
116  Result := 0;
117  if ENetPacketReliable in flag then
118    Inc( Result, Ord(ENET_PACKET_FLAG_RELIABLE) );
119  if ENetPacketUnsequenced in flag then
120    Inc( Result, Ord(ENET_PACKET_FLAG_UNSEQUENCED) );
121  if ENetPacketNoAllocate in flag then
122    Inc( Result, Ord(ENET_PACKET_FLAG_NO_ALLOCATE) );
123  if ENetPacketUnReliableFragment in flag then
124    Inc( Result, Ord(ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) );
125end;
126
127{ TENetClass }
128
129constructor TENetClass.Create( Port: word; bServer: Boolean );
130begin
131  FAddress.port := Port;
132  FMaxPeer := 100;
133  FMaxChannels := 255;
134  FBandwidthIncoming := 0;
135  FBandwidthOutgoing := 0;
136  FIsServer := False;
137  FConnectTimeout := 5000;
138  FMessageTimeout := 100;
139  FHost := nil;
140  FPeer := nil;
141  FEventNone := nil;
142  FEventConnect := nil;
143  FEventDisconnect := nil;
144  FEventReceive := nil;
145
146  FIsServer := bServer;
147  FInit := enet_initialize() = 0;
148end;
149
150destructor TENetClass.Destroy();
151begin
152  Disconnect(True);
153  if FInit then
154    enet_deinitialize();
155  inherited Destroy();
156end;
157
158function TENetClass.InitHost(): Boolean;
159begin
160  DeinitHost;
161  if FInit then begin
162    if FIsServer then begin
163      // for server
164      FAddress.host := ENET_HOST_ANY;
165      FHost := enet_host_create( @FAddress, FMaxPeer, FMaxChannels,
166        FBandwidthIncoming, FBandwidthOutgoing );
167    end else begin
168      // for client
169      FMaxPeer := 1;
170      FHost := enet_host_create( nil, FMaxPeer, FMaxChannels,
171        FBandwidthIncoming, FBandwidthOutgoing );
172    end;
173  end;
174  Result := FHost <> nil;
175end;
176
177procedure TENetClass.DeinitHost();
178begin
179  if FHost <> nil then
180    enet_host_destroy(FHost);
181  FHost := nil;
182end;
183
184function TENetClass.Connect( const Host: string; Port: Word ): Boolean;
185begin
186  Result := False;
187  if not FIsServer then begin
188    Disconnect(True);
189    InitHost();
190    enet_address_set_host( @FAddress, PAnsiChar(Host) );
191    FAddress.port := Port;
192
193    FClientData := Random(MaxInt);
194    FPeer := enet_host_connect( FHost, @FAddress, FMaxChannels, FClientData );
195
196    Result := FPeer <> nil;
197    if Result then begin
198      if (enet_host_service( FHost, @FEvent, FConnectTimeout ) > 0) and
199        (FEvent.kind = ENET_EVENT_TYPE_CONNECT) then
200      begin
201        Result := True;
202        if Assigned(FEventConnect) then
203          FEventConnect(FEvent);
204      end else begin
205        enet_peer_reset(FPeer);
206        FPeer := nil;
207        DeinitHost();
208      end;
209    end;
210  end;
211end;
212
213function TENetClass.Disconnect( bNow: Boolean ): Boolean;
214begin
215  Result := False;
216  if (not FIsServer) and (FHost <> nil) then begin
217    if FPeer <> nil then begin
218      if bNow then
219        enet_peer_disconnect_now( FPeer, FClientData )
220      else
221        enet_peer_disconnect( FPeer, FClientData );
222      FPeer := nil;
223    end;
224  Result := True;
225  end;
226  DeinitHost();
227end;
228
229function TENetClass.SendMsg( Channel: Byte; Data: Pointer; Length: Integer;
230  flag: TENetPacketFlags; WaitResponse: Boolean ): Boolean;
231var
232  FPacket : pENetPacket;
233  PacketFlag : Cardinal;
234begin
235  Result := False;
236  if FPeer <> nil then begin
237    PacketFlag := GetPacketFlag(flag);
238    FPacket := enet_packet_create( Data, Length, PacketFlag );
239    if enet_peer_send( FPeer, Channel, FPacket ) = 0 then
240      if WaitResponse then
241        Result := ProcessMsg();
242  end;
243end;
244
245function TENetClass.SendMsgEventPeer( Data: Pointer; Length: Integer;
246  flag: TENetPacketFlags; WaitResponse: Boolean ): Boolean;
247var
248  FPacket : pENetPacket;
249  PacketFlag : Cardinal;
250begin
251  Result := False;
252  if FEvent.Peer <> nil then begin
253    PacketFlag := GetPacketFlag(flag);
254    FPacket := enet_packet_create( Data, Length, PacketFlag );
255    if enet_peer_send( FEvent.Peer, FEvent.channelID, FPacket ) = 0 then
256      if WaitResponse then
257        Result := ProcessMsg();
258  end;
259end;
260
261procedure TENetClass.FlushMsg();
262begin
263  if FHost <> nil then
264    enet_host_flush(FHost);
265end;
266
267function TENetClass.BroadcastMsg( Channel: Byte; Data: Pointer; Length: Integer;
268  flag: TENetPacketFlags; WaitResponse: Boolean ): Boolean;
269var
270  FPacket : pENetPacket;
271  PacketFlag : Cardinal;
272begin
273  Result := False;
274  if FPeer <> nil then begin
275     PacketFlag := GetPacketFlag(flag);
276     FPacket := enet_packet_create( Data, Length, PacketFlag );
277     enet_host_broadcast( FHost, Channel, FPacket );
278     if WaitResponse then
279       Result := ProcessMsg();
280  end;
281end;
282
283function TENetClass.ProcessMsg(): Boolean;
284var
285  broadcast : Boolean;
286  bdChannel : Byte;
287  packet : pENetPacket;
288  pflag : Integer;
289  svcres : integer;
290begin
291  Result := False;
292  if FHost <> nil then begin
293    SvcRes := enet_host_service( FHost, @FEvent, FMessageTimeout );
294    if SvcRes > 0 then begin
295      case FEvent.kind of
296        ENET_EVENT_TYPE_NONE:
297          if Assigned(FEventNone) then
298            FEventNone(FEvent);
299        ENET_EVENT_TYPE_CONNECT:
300          if Assigned(FEventConnect) then
301            FEventConnect(FEvent);
302        ENET_EVENT_TYPE_DISCONNECT:
303          if Assigned(FEventDisconnect) then
304            FEventDisconnect(FEvent);
305        ENET_EVENT_TYPE_RECEIVE: begin
306          try
307            if FIsServer then begin
308               broadcast := True;
309               pflag := FEvent.packet^.flags;
310               bdChannel := FEvent.channelID;
311            end;
312            if Assigned(FEventReceive) then
313               FEventReceive( FEvent, broadcast, bdChannel );
314            if FIsServer and broadcast then begin
315               packet := enet_packet_create( FEvent.packet^.data,
316                 FEvent.packet^.dataLength, pflag );
317               enet_host_broadcast( FHost, bdChannel, packet );
318            end;
319          finally
320            enet_packet_destroy( FEvent.packet );
321          end;
322        end;
323      end;
324      Result := True;
325    end;
326  end;
327end;
328
329procedure TENetClass.Ping();
330begin
331  if (not FIsServer) and (FPeer <> nil) then
332    enet_peer_ping(FPeer);
333end;
334
335end.
336
337