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