1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE g_amodes.inc}
22 e_log
, e_fixedbuffer
, ENet
, Classes
;
25 NET_PROTOCOL_VER
= 172;
30 NET_CHAN_IMPORTANT
= 1;
33 NET_CHAN_PLAYERPOS
= 4;
35 NET_CHAN_MONSTERPOS
= 6;
36 NET_CHAN_LARGEDATA
= 7;
38 NET_CHAN_DOWNLOAD
= 9;
44 CH_MAX
= CH_UNRELIABLE
; // don't change this
56 NET_DISC_NONE
: enet_uint32
= 0;
57 NET_DISC_PROTOCOL
: enet_uint32
= 1;
58 NET_DISC_VERSION
: enet_uint32
= 2;
59 NET_DISC_FULL
: enet_uint32
= 3;
60 NET_DISC_KICK
: enet_uint32
= 4;
61 NET_DISC_DOWN
: enet_uint32
= 5;
62 NET_DISC_PASSWORD
: enet_uint32
= 6;
63 NET_DISC_TEMPBAN
: enet_uint32
= 7;
64 NET_DISC_BAN
: enet_uint32
= 8;
65 NET_DISC_MAX
: enet_uint32
= 8;
71 BANLIST_FILENAME
= 'banlist.txt';
72 NETDUMP_FILENAME
= 'netdump';
81 RequestedFullUpdate
: Boolean;
84 SendBuf
: array [0..CH_MAX
] of TBuffer
;
90 pTNetClient
= ^TNetClient
;
92 AByte
= array of Byte;
95 NetInitDone
: Boolean = False;
96 NetMode
: Byte = NET_NONE
;
97 NetDump
: Boolean = False;
99 NetServerName
: string = 'Unnamed Server';
100 NetPassword
: string = '';
101 NetPort
: Word = 25666;
103 NetAllowRCON
: Boolean = False;
104 NetRCONPassword
: string = '';
106 NetTimeToUpdate
: Cardinal = 0;
107 NetTimeToReliable
: Cardinal = 0;
108 NetTimeToMaster
: Cardinal = 0;
110 NetHost
: pENetHost
= nil;
111 NetPeer
: pENetPeer
= nil;
113 NetAddr
: ENetAddress
;
115 NetPongAddr
: ENetAddress
;
116 NetPongSock
: ENetSocket
= ENET_SOCKET_NULL
;
118 NetUseMaster
: Boolean = True;
119 NetSlistAddr
: ENetAddress
;
120 NetSlistIP
: string = 'mpms.doom2d.org';
121 NetSlistPort
: Word = 25665;
123 NetClientIP
: string = '127.0.0.1';
124 NetClientPort
: Word = 25666;
126 NetIn
, NetOut
: TBuffer
;
127 NetSend
: array [0..CH_MAX
] of TBuffer
;
129 NetClients
: array of TNetClient
= nil;
130 NetClientCount
: Byte = 0;
131 NetMaxClients
: Byte = 255;
132 NetBannedHosts
: array of TBanRecord
= nil;
134 NetState
: Integer = NET_STATE_NONE
;
136 NetMyID
: Integer = -1;
137 NetPlrUID1
: Integer = -1;
138 NetPlrUID2
: Integer = -1;
140 NetInterpLevel
: Integer = 1;
141 NetUpdateRate
: Cardinal = 0; // as soon as possible
142 NetRelupdRate
: Cardinal = 18; // around two times a second
143 NetMasterRate
: Cardinal = 60000;
145 NetForcePlayerUpdate
: Boolean = False;
146 NetPredictSelf
: Boolean = True;
147 NetGotKeys
: Boolean = False;
149 NetGotEverything
: Boolean = False;
151 NetDumpFile
: TStream
;
153 function g_Net_Init(): Boolean;
154 procedure g_Net_Cleanup();
155 procedure g_Net_Free();
156 procedure g_Net_Flush();
158 function g_Net_Host(IPAddr
: LongWord; Port
: enet_uint16
; MaxClients
: Cardinal = 16): Boolean;
159 procedure g_Net_Host_Die();
160 procedure g_Net_Host_Send(ID
: Integer; Reliable
: Boolean; Chan
: Byte = NET_CHAN_GAME
);
161 function g_Net_Host_Update(): enet_size_t
;
162 procedure g_Net_Host_FlushBuffers();
164 function g_Net_Connect(IP
: string; Port
: enet_uint16
): Boolean;
165 procedure g_Net_Disconnect(Forced
: Boolean = False);
166 procedure g_Net_Client_Send(Reliable
: Boolean; Chan
: Byte = NET_CHAN_GAME
);
167 function g_Net_Client_Update(): enet_size_t
;
168 function g_Net_Client_UpdateWhileLoading(): enet_size_t
;
169 procedure g_Net_Client_FlushBuffers();
171 function g_Net_Client_ByName(Name
: string): pTNetClient
;
172 function g_Net_Client_ByPlayer(PID
: Word): pTNetClient
;
173 function g_Net_ClientName_ByID(ID
: Integer): string;
175 procedure g_Net_SendData(Data
:AByte
; peer
: pENetPeer
; Reliable
: Boolean; Chan
: Byte = NET_CHAN_DOWNLOAD
);
176 function g_Net_Wait_Event(msgId
: Word): TMemoryStream
;
178 function IpToStr(IP
: LongWord): string;
179 function StrToIp(IPstr
: string; var IP
: LongWord): Boolean;
181 function g_Net_IsHostBanned(IP
: LongWord; Perm
: Boolean = False): Boolean;
182 procedure g_Net_BanHost(IP
: LongWord; Perm
: Boolean = True); overload
;
183 procedure g_Net_BanHost(IP
: string; Perm
: Boolean = True); overload
;
184 function g_Net_UnbanHost(IP
: string): Boolean; overload
;
185 function g_Net_UnbanHost(IP
: LongWord): Boolean; overload
;
186 procedure g_Net_UnbanNonPermHosts();
187 procedure g_Net_SaveBanList();
189 procedure g_Net_DumpStart();
190 procedure g_Net_DumpSendBuffer(Buf
: pTBuffer
);
191 procedure g_Net_DumpRecvBuffer(Buf
: penet_uint8
; Len
: LongWord);
192 procedure g_Net_DumpEnd();
198 e_input
, g_nethandler
, g_netmsg
, g_netmaster
, g_player
, g_window
, g_console
,
199 g_main
, g_game
, g_language
, g_weapons
, utils
;
202 { /// SERVICE FUNCTIONS /// }
205 procedure SendBuffer(B
: pTBuffer
; Ch
: Integer; Peer
: pENetPeer
);
210 if Ch
= CH_RELIABLE
then Fl
:= ENET_PACKET_FLAG_RELIABLE
212 if B
^.WritePos
> 0 then
214 P
:= enet_packet_create(Addr(B
^.Data
), B
^.WritePos
, Fl
);
218 enet_host_broadcast(NetHost
, Ch
, P
)
220 enet_peer_send(Peer
, Ch
, P
);
222 if NetDump
then g_Net_DumpSendBuffer(B
);
227 function g_Net_FindSlot(): Integer;
236 for I
:= Low(NetClients
) to High(NetClients
) do
238 if NetClients
[I
].Used
then
247 if C
>= NetMaxClients
then
255 if (Length(NetClients
) >= NetMaxClients
) then
259 SetLength(NetClients
, Length(NetClients
) + 1);
260 N
:= High(NetClients
);
266 NetClients
[N
].Used
:= True;
267 NetClients
[N
].ID
:= N
;
268 NetClients
[N
].RequestedFullUpdate
:= False;
269 NetClients
[N
].RCONAuth
:= False;
270 NetClients
[N
].Voted
:= False;
271 NetClients
[N
].Player
:= 0;
272 NetClients
[N
].Peer
:= nil;
273 for I
:= 0 to CH_MAX
do
274 e_Buffer_Clear(Addr(NetClients
[N
].SendBuf
[CH_MAX
]));
280 function g_Net_Init(): Boolean;
287 e_Buffer_Clear(@NetIn
);
288 e_Buffer_Clear(@NetOut
);
289 for I
:= 0 to CH_MAX
do
290 e_Buffer_Clear(@NetSend
[i
]);
291 SetLength(NetClients
, 0);
297 NetAddr
.port
:= 25666;
298 SetLength(NetBannedHosts
, 0);
299 if FileExists(DataDir
+ BANLIST_FILENAME
) then
301 Assign(F
, DataDir
+ BANLIST_FILENAME
);
306 if StrToIp(IPstr
, IP
) then
313 Result
:= (enet_initialize() = 0);
316 procedure g_Net_Flush();
318 if NetMode
= NET_SERVER
then
319 g_Net_Host_FlushBuffers()
321 g_Net_Client_FlushBuffers();
322 enet_host_flush(NetHost
);
325 procedure g_Net_Cleanup();
329 e_Buffer_Clear(@NetIn
);
330 e_Buffer_Clear(@NetOut
);
331 for i
:= 0 to CH_MAX
do
332 e_Buffer_Clear(@NetSend
[i
]);
334 SetLength(NetClients
, 0);
344 NetState
:= NET_STATE_NONE
;
346 NetPongSock
:= ENET_SOCKET_NULL
;
348 NetTimeToMaster
:= 0;
349 NetTimeToUpdate
:= 0;
350 NetTimeToReliable
:= 0;
358 procedure g_Net_Free();
363 NetInitDone
:= False;
367 { /// SERVER FUNCTIONS /// }
370 function g_Net_Host(IPAddr
: LongWord; Port
: enet_uint16
; MaxClients
: Cardinal = 16): Boolean;
372 if NetMode
<> NET_NONE
then
374 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_INGAME
]);
381 g_Console_Add(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_MSG_HOST
], [Port
]));
382 if not NetInitDone
then
384 if (not g_Net_Init()) then
386 g_Console_Add(_lc
[I_NET_MSG_FERROR
] + _lc
[I_NET_ERR_ENET
]);
394 NetAddr
.host
:= IPAddr
;
395 NetAddr
.port
:= Port
;
397 NetHost
:= enet_host_create(@NetAddr
, NET_MAXCLIENTS
, NET_CHANS
, 0, 0);
399 if (NetHost
= nil) then
401 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + Format(_lc
[I_NET_ERR_HOST
], [Port
]));
407 NetPongSock
:= enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM
);
408 if NetPongSock
<> ENET_SOCKET_NULL
then
410 NetPongAddr
.host
:= IPAddr
;
411 NetPongAddr
.port
:= Port
+ 1;
412 if enet_socket_bind(NetPongSock
, @NetPongAddr
) < 0 then
414 enet_socket_destroy(NetPongSock
);
415 NetPongSock
:= ENET_SOCKET_NULL
;
418 enet_socket_set_option(NetPongSock
, ENET_SOCKOPT_NONBLOCK
, 1);
421 NetMode
:= NET_SERVER
;
422 e_Buffer_Clear(@NetOut
);
428 procedure g_Net_Host_Die();
432 if NetMode
<> NET_SERVER
then Exit
;
434 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_DISCALL
]);
435 for I
:= 0 to High(NetClients
) do
436 if NetClients
[I
].Used
then
437 enet_peer_disconnect(NetClients
[I
].Peer
, NET_DISC_DOWN
);
439 while enet_host_service(NetHost
, @NetEvent
, 1000) > 0 do
440 if NetEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
then
441 enet_packet_destroy(NetEvent
.packet
);
443 for I
:= 0 to High(NetClients
) do
444 if NetClients
[I
].Used
then
446 FreeMemory(NetClients
[I
].Peer
^.data
);
447 NetClients
[I
].Peer
^.data
:= nil;
448 enet_peer_reset(NetClients
[I
].Peer
);
449 NetClients
[I
].Peer
:= nil;
450 NetClients
[I
].Used
:= False;
453 if (NetMPeer
<> nil) and (NetMHost
<> nil) then g_Net_Slist_Disconnect
;
454 if NetPongSock
<> ENET_SOCKET_NULL
then
455 enet_socket_destroy(NetPongSock
);
457 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_DIE
]);
458 enet_host_destroy(NetHost
);
463 e_WriteLog('NET: Server stopped', MSG_NOTIFY
);
466 procedure g_Net_Host_FlushBuffers();
471 SendBuffer(@NetSend
[CH_RELIABLE
], CH_RELIABLE
, nil);
472 SendBuffer(@NetSend
[CH_UNRELIABLE
], CH_UNRELIABLE
, nil);
473 // send to individual clients
474 if NetClients
<> nil then
475 for I
:= Low(NetClients
) to High(NetClients
) do
476 with NetClients
[I
] do
478 SendBuffer(@SendBuf
[CH_RELIABLE
], CH_RELIABLE
, Peer
);
479 SendBuffer(@SendBuf
[CH_UNRELIABLE
], CH_UNRELIABLE
, Peer
);
483 procedure g_Net_Host_Send(ID
: Integer; Reliable
: Boolean; Chan
: Byte = NET_CHAN_GAME
);
495 if (ID
> High(NetClients
)) or (NetClients
[ID
].Peer
= nil) then
497 e_Buffer_Clear(@NetOut
);
500 B
:= Addr(NetClients
[ID
].SendBuf
[I
]);
504 B
:= Addr(NetSend
[I
]);
507 e_Buffer_Write(B
, @NetOut
);
508 e_Buffer_Clear(@NetOut
);
511 procedure g_Net_Host_CheckPings();
517 Ping
: array [0..9] of Byte;
520 if NetPongSock
= ENET_SOCKET_NULL
then Exit
;
522 Buf
.data
:= Addr(Ping
[0]);
523 Buf
.dataLength
:= 2+8;
527 Len
:= enet_socket_receive(NetPongSock
, @ClAddr
, @Buf
, 1);
528 if Len
< 0 then Exit
;
530 if (Ping
[0] = Ord('D')) and (Ping
[1] = Ord('F')) then
532 ClTime
:= Int64(Addr(Ping
[2])^);
534 e_Buffer_Clear(@NetOut
);
535 e_Buffer_Write(@NetOut
, Byte(Ord('D')));
536 e_Buffer_Write(@NetOut
, Byte(Ord('F')));
537 e_Buffer_Write(@NetOut
, ClTime
);
538 g_Net_Slist_WriteInfo();
540 if gPlayer1
<> nil then Inc(NPl
);
541 if gPlayer2
<> nil then Inc(NPl
);
542 e_Buffer_Write(@NetOut
, NPl
);
543 e_Buffer_Write(@NetOut
, gNumBots
);
545 Buf
.data
:= Addr(NetOut
.Data
[0]);
546 Buf
.dataLength
:= NetOut
.WritePos
;
547 enet_socket_send(NetPongSock
, @ClAddr
, @Buf
, 1);
549 e_Buffer_Clear(@NetOut
);
553 function g_Net_Host_Update(): enet_size_t
;
567 g_Net_Host_CheckPings
;
570 while (enet_host_service(NetHost
, @NetEvent
, 0) > 0) do
572 case (NetEvent
.kind
) of
573 ENET_EVENT_TYPE_CONNECT
:
575 IP
:= IpToStr(NetEvent
.Peer
^.address
.host
);
576 Port
:= NetEvent
.Peer
^.address
.port
;
577 g_Console_Add(_lc
[I_NET_MSG
] +
578 Format(_lc
[I_NET_MSG_HOST_CONN
], [IP
, Port
]));
580 if (NetEvent
.data
<> NET_PROTOCOL_VER
) then
582 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
583 _lc
[I_NET_DISC_PROTOCOL
]);
584 NetEvent
.peer
^.data
:= GetMemory(SizeOf(Byte));
585 Byte(NetEvent
.peer
^.data
^) := 255;
586 enet_peer_disconnect(NetEvent
.peer
, NET_DISC_PROTOCOL
);
587 enet_host_flush(NetHost
);
591 ID
:= g_Net_FindSlot();
595 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_HOST_REJECT
] +
596 _lc
[I_NET_DISC_FULL
]);
597 NetEvent
.Peer
^.data
:= GetMemory(SizeOf(Byte));
598 Byte(NetEvent
.peer
^.data
^) := 255;
599 enet_peer_disconnect(NetEvent
.peer
, NET_DISC_FULL
);
600 enet_host_flush(NetHost
);
604 NetClients
[ID
].Peer
:= NetEvent
.peer
;
605 NetClients
[ID
].Peer
^.data
:= GetMemory(SizeOf(Byte));
606 Byte(NetClients
[ID
].Peer
^.data
^) := ID
;
607 NetClients
[ID
].State
:= NET_STATE_AUTH
;
608 NetClients
[ID
].RCONAuth
:= False;
610 enet_peer_timeout(NetEvent
.peer
, ENET_PEER_TIMEOUT_LIMIT
* 2, ENET_PEER_TIMEOUT_MINIMUM
* 2, ENET_PEER_TIMEOUT_MAXIMUM
* 2);
613 g_Console_Add(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_MSG_HOST_ADD
], [ID
]));
616 ENET_EVENT_TYPE_RECEIVE
:
618 ID
:= Byte(NetEvent
.peer
^.data
^);
619 if ID
> High(NetClients
) then Exit
;
620 TC
:= @NetClients
[ID
];
622 g_Net_DumpRecvBuffer(NetEvent
.packet
^.data
, NetEvent
.packet
^.dataLength
);
623 g_Net_HostMsgHandler(TC
, NetEvent
.packet
);
626 ENET_EVENT_TYPE_DISCONNECT
:
628 ID
:= Byte(NetEvent
.peer
^.data
^);
629 if ID
> High(NetClients
) then Exit
;
630 TC
:= @NetClients
[ID
];
631 if TC
= nil then Exit
;
633 if not (TC
^.Used
) then Exit
;
635 TP
:= g_Player_Get(TC
^.Player
);
640 TP
.Kill(K_SIMPLEKILL
, 0, HIT_DISCON
);
641 g_Console_Add(Format(_lc
[I_PLAYER_LEAVE
], [TP
.Name
]), True);
642 e_WriteLog('NET: Client ' + TP
.Name
+ ' [' + IntToStr(ID
) + '] disconnected.', MSG_NOTIFY
);
643 g_Player_Remove(TP
.UID
);
647 TC
^.State
:= NET_STATE_NONE
;
650 TC
^.RequestedFullUpdate
:= False;
652 FreeMemory(NetEvent
.peer
^.data
);
653 NetEvent
.peer
^.data
:= nil;
654 g_Console_Add(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_MSG_HOST_DISC
], [ID
]));
657 if NetUseMaster
then g_Net_Slist_Update
;
664 { /// CLIENT FUNCTIONS /// }
667 procedure g_Net_Disconnect(Forced
: Boolean = False);
669 if NetMode
<> NET_CLIENT
then Exit
;
670 if (NetHost
= nil) or (NetPeer
= nil) then Exit
;
674 enet_peer_disconnect(NetPeer
, NET_DISC_NONE
);
676 while (enet_host_service(NetHost
, @NetEvent
, 1500) > 0) do
678 if (NetEvent
.kind
= ENET_EVENT_TYPE_DISCONNECT
) then
684 if (NetEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
) then
685 enet_packet_destroy(NetEvent
.packet
);
688 if NetPeer
<> nil then
690 enet_peer_reset(NetPeer
);
696 e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent
.data
), MSG_NOTIFY
);
697 if (NetEvent
.data
<= NET_DISC_MAX
) then
698 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_KICK
] +
699 _lc
[TStrings_Locale(Cardinal(I_NET_DISC_NONE
) + NetEvent
.data
)], True);
702 if NetHost
<> nil then
704 enet_host_destroy(NetHost
);
707 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_CLIENT_DISC
]);
710 e_WriteLog('NET: Disconnected', MSG_NOTIFY
);
713 procedure g_Net_Client_FlushBuffers();
715 SendBuffer(@NetSend
[CH_RELIABLE
], CH_RELIABLE
, NetPeer
);
716 SendBuffer(@NetSend
[CH_UNRELIABLE
], CH_UNRELIABLE
, NetPeer
);
719 procedure g_Net_Client_Send(Reliable
: Boolean; Chan
: Byte = NET_CHAN_GAME
);
727 e_Buffer_Write(@NetSend
[I
], @NetOut
);
728 e_Buffer_Clear(@NetOut
);
731 function g_Net_Client_Update(): enet_size_t
;
734 while (enet_host_service(NetHost
, @NetEvent
, 0) > 0) do
736 case NetEvent
.kind
of
737 ENET_EVENT_TYPE_RECEIVE
:
740 g_Net_DumpRecvBuffer(NetEvent
.packet
^.data
, NetEvent
.packet
^.dataLength
);
741 g_Net_ClientMsgHandler(NetEvent
.packet
);
744 ENET_EVENT_TYPE_DISCONNECT
:
746 g_Net_Disconnect(True);
754 function g_Net_Client_UpdateWhileLoading(): enet_size_t
;
757 while (enet_host_service(NetHost
, @NetEvent
, 0) > 0) do
759 case NetEvent
.kind
of
760 ENET_EVENT_TYPE_RECEIVE
:
763 g_Net_DumpRecvBuffer(NetEvent
.packet
^.data
, NetEvent
.packet
^.dataLength
);
764 g_Net_ClientLightMsgHandler(NetEvent
.packet
);
767 ENET_EVENT_TYPE_DISCONNECT
:
769 g_Net_Disconnect(True);
777 function g_Net_Connect(IP
: string; Port
: enet_uint16
): Boolean;
781 if NetMode
<> NET_NONE
then
783 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_ERR_INGAME
], True);
790 g_Console_Add(_lc
[I_NET_MSG
] + Format(_lc
[I_NET_MSG_CLIENT_CONN
],
792 if not NetInitDone
then
794 if (not g_Net_Init()) then
796 g_Console_Add(_lc
[I_NET_MSG_FERROR
] + _lc
[I_NET_ERR_ENET
], True);
804 NetHost
:= enet_host_create(nil, 1, NET_CHANS
, 0, 0);
806 if (NetHost
= nil) then
808 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CLIENT
], True);
814 enet_address_set_host(@NetAddr
, PChar(Addr(IP
[1])));
815 NetAddr
.port
:= Port
;
817 NetPeer
:= enet_host_connect(NetHost
, @NetAddr
, NET_CHANS
, NET_PROTOCOL_VER
);
819 if (NetPeer
= nil) then
821 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CLIENT
], True);
822 enet_host_destroy(NetHost
);
831 while (enet_host_service(NetHost
, @NetEvent
, 0) > 0) do
833 if (NetEvent
.kind
= ENET_EVENT_TYPE_CONNECT
) then
835 g_Console_Add(_lc
[I_NET_MSG
] + _lc
[I_NET_MSG_CLIENT_DONE
]);
836 NetMode
:= NET_CLIENT
;
837 e_Buffer_Clear(@NetOut
);
838 enet_peer_timeout(NetPeer
, ENET_PEER_TIMEOUT_LIMIT
* 2, ENET_PEER_TIMEOUT_MINIMUM
* 2, ENET_PEER_TIMEOUT_MAXIMUM
* 2);
840 NetClientPort
:= Port
;
851 if e_KeyPressed(IK_ESCAPE
) or e_KeyPressed(IK_SPACE
) then
855 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_TIMEOUT
], True);
856 if NetPeer
<> nil then enet_peer_reset(NetPeer
);
857 if NetHost
<> nil then
859 enet_host_destroy(NetHost
);
866 function IpToStr(IP
: LongWord): string;
874 Result
:= Result
+ IntToStr(e_Raw_Read_Byte(Ptr
)) + '.';
875 Result
:= Result
+ IntToStr(e_Raw_Read_Byte(Ptr
)) + '.';
876 Result
:= Result
+ IntToStr(e_Raw_Read_Byte(Ptr
)) + '.';
877 Result
:= Result
+ IntToStr(e_Raw_Read_Byte(Ptr
));
881 function StrToIp(IPstr
: string; var IP
: LongWord): Boolean;
885 Result
:= enet_address_set_host(@EAddr
, PChar(@IPstr
[1])) = 0;
889 function g_Net_Client_ByName(Name
: string): pTNetClient
;
895 for a
:= Low(NetClients
) to High(NetClients
) do
896 if (NetClients
[a
].Used
) and (NetClients
[a
].State
= NET_STATE_GAME
) then
898 pl
:= g_Player_Get(NetClients
[a
].Player
);
899 if pl
= nil then continue
;
900 if Copy(LowerCase(pl
.Name
), 1, Length(Name
)) <> LowerCase(Name
) then continue
;
901 if NetClients
[a
].Peer
<> nil then
903 Result
:= @NetClients
[a
];
909 function g_Net_Client_ByPlayer(PID
: Word): pTNetClient
;
914 for a
:= Low(NetClients
) to High(NetClients
) do
915 if (NetClients
[a
].Used
) and (NetClients
[a
].State
= NET_STATE_GAME
) then
916 if NetClients
[a
].Player
= PID
then
918 Result
:= @NetClients
[a
];
923 function g_Net_ClientName_ByID(ID
: Integer): string;
929 if ID
= NET_EVERYONE
then
931 for a
:= Low(NetClients
) to High(NetClients
) do
932 if (NetClients
[a
].ID
= ID
) and (NetClients
[a
].Used
) and (NetClients
[a
].State
= NET_STATE_GAME
) then
934 pl
:= g_Player_Get(NetClients
[a
].Player
);
935 if pl
= nil then Exit
;
940 procedure g_Net_SendData(Data
:AByte
; peer
: pENetPeer
; Reliable
: Boolean; Chan
: Byte = NET_CHAN_DOWNLOAD
);
944 dataLength
: Cardinal;
946 dataLength
:= Length(Data
);
949 F
:= LongWord(ENET_PACKET_FLAG_RELIABLE
)
953 if (peer
<> nil) then
955 P
:= enet_packet_create(@Data
[0], dataLength
, F
);
956 if not Assigned(P
) then Exit
;
957 enet_peer_send(peer
, CH_DOWNLOAD
, P
);
961 P
:= enet_packet_create(@Data
[0], dataLength
, F
);
962 if not Assigned(P
) then Exit
;
963 enet_host_broadcast(NetHost
, CH_DOWNLOAD
, P
);
966 enet_host_flush(NetHost
);
969 function g_Net_Wait_Event(msgId
: Word): TMemoryStream
;
971 downloadEvent
: ENetEvent
;
976 msgStream
: TMemoryStream
;
978 FillChar(downloadEvent
, SizeOf(downloadEvent
), 0);
983 while (enet_host_service(NetHost
, @downloadEvent
, 0) > 0) do
985 if (downloadEvent
.kind
= ENET_EVENT_TYPE_RECEIVE
) and (downloadEvent
.packet
^.dataLength
> 2) then
987 Len
:= PWord(downloadEvent
.packet
^.data
)^;
988 if Len
= 0 then break
;
989 Ptr
:= downloadEvent
.packet
^.data
+ 2; // skip length
992 if (MID
= msgId
) then
994 msgStream
:= TMemoryStream
.Create
;
995 msgStream
.SetSize(downloadEvent
.packet
^.dataLength
- 2);
996 msgStream
.WriteBuffer(Ptr
^, downloadEvent
.packet
^.dataLength
- 2);
997 msgStream
.Seek(0, soFromBeginning
);
1000 enet_packet_destroy(downloadEvent
.packet
);
1004 enet_packet_destroy(downloadEvent
.packet
);
1008 if (downloadEvent
.kind
= ENET_EVENT_TYPE_DISCONNECT
) then
1010 if (downloadEvent
.data
<= NET_DISC_MAX
) then
1011 g_Console_Add(_lc
[I_NET_MSG_ERROR
] + _lc
[I_NET_ERR_CONN
] + ' ' +
1012 _lc
[TStrings_Locale(Cardinal(I_NET_DISC_NONE
) + downloadEvent
.data
)], True);
1022 if e_KeyPressed(IK_ESCAPE
) or e_KeyPressed(IK_SPACE
) then
1025 Result
:= msgStream
;
1028 function g_Net_IsHostBanned(IP
: LongWord; Perm
: Boolean = False): Boolean;
1033 if NetBannedHosts
= nil then
1035 for I
:= 0 to High(NetBannedHosts
) do
1036 if (NetBannedHosts
[I
].IP
= IP
) and ((not Perm
) or (NetBannedHosts
[I
].Perm
)) then
1043 procedure g_Net_BanHost(IP
: LongWord; Perm
: Boolean = True); overload
;
1049 if g_Net_IsHostBanned(IP
, Perm
) then
1053 for I
:= Low(NetBannedHosts
) to High(NetBannedHosts
) do
1054 if NetBannedHosts
[I
].IP
= 0 then
1062 SetLength(NetBannedHosts
, Length(NetBannedHosts
) + 1);
1063 P
:= High(NetBannedHosts
);
1066 NetBannedHosts
[P
].IP
:= IP
;
1067 NetBannedHosts
[P
].Perm
:= Perm
;
1070 procedure g_Net_BanHost(IP
: string; Perm
: Boolean = True); overload
;
1075 b
:= StrToIp(IP
, a
);
1077 g_Net_BanHost(a
, Perm
);
1080 procedure g_Net_UnbanNonPermHosts();
1084 if NetBannedHosts
= nil then
1086 for I
:= Low(NetBannedHosts
) to High(NetBannedHosts
) do
1087 if (NetBannedHosts
[I
].IP
> 0) and not NetBannedHosts
[I
].Perm
then
1089 NetBannedHosts
[I
].IP
:= 0;
1090 NetBannedHosts
[I
].Perm
:= True;
1094 function g_Net_UnbanHost(IP
: string): Boolean; overload
;
1098 Result
:= StrToIp(IP
, a
);
1100 Result
:= g_Net_UnbanHost(a
);
1103 function g_Net_UnbanHost(IP
: LongWord): Boolean; overload
;
1110 if NetBannedHosts
= nil then
1112 for I
:= 0 to High(NetBannedHosts
) do
1113 if NetBannedHosts
[I
].IP
= IP
then
1115 NetBannedHosts
[I
].IP
:= 0;
1116 NetBannedHosts
[I
].Perm
:= True;
1118 // no break here to clear all bans of this host, perm and non-perm
1122 procedure g_Net_SaveBanList();
1127 Assign(F
, DataDir
+ BANLIST_FILENAME
);
1129 if NetBannedHosts
<> nil then
1130 for I
:= 0 to High(NetBannedHosts
) do
1131 if NetBannedHosts
[I
].Perm
and (NetBannedHosts
[I
].IP
> 0) then
1132 Writeln(F
, IpToStr(NetBannedHosts
[I
].IP
));
1136 procedure g_Net_DumpStart();
1138 if NetMode
= NET_SERVER
then
1139 NetDumpFile
:= createDiskFile(NETDUMP_FILENAME
+ '_server')
1141 NetDumpFile
:= createDiskFile(NETDUMP_FILENAME
+ '_client');
1144 procedure g_Net_DumpSendBuffer(Buf
: pTBuffer
);
1146 writeInt(NetDumpFile
, Byte($BA));
1147 writeInt(NetDumpFile
, Byte($BE));
1148 writeInt(NetDumpFile
, Byte($FF));
1149 writeInt(NetDumpFile
, gTime
);
1150 writeInt(NetDumpFile
, Byte($FF));
1151 writeInt(NetDumpFile
, LongWord(Buf
^.WritePos
));
1152 writeInt(NetDumpFile
, Byte($FF));
1153 NetDumpFile
.WriteBuffer(Buf
^.Data
[0], Buf
^.WritePos
);
1156 procedure g_Net_DumpRecvBuffer(Buf
: penet_uint8
; Len
: LongWord);
1158 if (Buf
= nil) or (Len
= 0) then Exit
;
1159 writeInt(NetDumpFile
, Byte($B0));
1160 writeInt(NetDumpFile
, Byte($0B));
1161 writeInt(NetDumpFile
, Byte($FF));
1162 writeInt(NetDumpFile
, gTime
);
1163 writeInt(NetDumpFile
, Byte($FF));
1164 writeInt(NetDumpFile
, Len
);
1165 writeInt(NetDumpFile
, Byte($FF));
1166 NetDumpFile
.WriteBuffer(Buf
^, Len
);
1169 procedure g_Net_DumpEnd();