DEADSOFTWARE

more buffer fixes (?)
[d2df-sdl.git] / src / game / g_net.pas
1 (* Copyright (C) DooM 2D:Forever Developers
2 *
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.
7 *
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.
12 *
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/>.
15 *)
16 {$INCLUDE g_amodes.inc}
17 unit g_net;
19 interface
21 uses
22 e_log, e_fixedbuffer, ENet, Classes;
24 const
25 NET_PROTOCOL_VER = 172;
27 NET_MAXCLIENTS = 24;
29 NET_CHAN_SERVICE = 0;
30 NET_CHAN_IMPORTANT = 1;
31 NET_CHAN_GAME = 2;
32 NET_CHAN_PLAYER = 3;
33 NET_CHAN_PLAYERPOS = 4;
34 NET_CHAN_MONSTER = 5;
35 NET_CHAN_MONSTERPOS = 6;
36 NET_CHAN_LARGEDATA = 7;
37 NET_CHAN_CHAT = 8;
38 NET_CHAN_DOWNLOAD = 9;
39 NET_CHAN_SHOTS = 10;
41 CH_RELIABLE = 0;
42 CH_UNRELIABLE = 1;
43 CH_DOWNLOAD = 2;
44 CH_MAX = CH_UNRELIABLE; // don't change this
46 NET_CHANS = 3;
48 NET_NONE = 0;
49 NET_SERVER = 1;
50 NET_CLIENT = 2;
52 NET_BUFSIZE = 65536;
54 NET_EVERYONE = -1;
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;
67 NET_STATE_NONE = 0;
68 NET_STATE_AUTH = 1;
69 NET_STATE_GAME = 2;
71 BANLIST_FILENAME = 'banlist.txt';
72 NETDUMP_FILENAME = 'netdump';
74 type
75 TNetClient = record
76 ID: Byte;
77 Used: Boolean;
78 State: Byte;
79 Peer: pENetPeer;
80 Player: Word;
81 RequestedFullUpdate: Boolean;
82 RCONAuth: Boolean;
83 Voted: Boolean;
84 SendBuf: array [0..CH_MAX] of TBuffer;
85 end;
86 TBanRecord = record
87 IP: LongWord;
88 Perm: Boolean;
89 end;
90 pTNetClient = ^TNetClient;
92 AByte = array of Byte;
94 var
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;
112 NetEvent: ENetEvent;
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();
194 implementation
196 uses
197 SysUtils,
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);
206 var
207 P: pENetPacket;
208 Fl: enet_uint32;
209 begin
210 if Ch = CH_RELIABLE then Fl := ENET_PACKET_FLAG_RELIABLE
211 else Fl := 0;
212 if B^.WritePos > 0 then
213 begin
214 P := enet_packet_create(Addr(B^.Data), B^.WritePos, Fl);
215 if P <> nil then
216 begin
217 if Peer = nil then
218 enet_host_broadcast(NetHost, Ch, P)
219 else
220 enet_peer_send(Peer, Ch, P);
221 end;
222 if NetDump then g_Net_DumpSendBuffer(B);
223 e_Buffer_Clear(B);
224 end;
225 end;
227 function g_Net_FindSlot(): Integer;
228 var
229 I: Integer;
230 F: Boolean;
231 N, C: Integer;
232 begin
233 N := -1;
234 F := False;
235 C := 0;
236 for I := Low(NetClients) to High(NetClients) do
237 begin
238 if NetClients[I].Used then
239 Inc(C)
240 else
241 if not F then
242 begin
243 F := True;
244 N := I;
245 end;
246 end;
247 if C >= NetMaxClients then
248 begin
249 Result := -1;
250 Exit;
251 end;
253 if not F then
254 begin
255 if (Length(NetClients) >= NetMaxClients) then
256 N := -1
257 else
258 begin
259 SetLength(NetClients, Length(NetClients) + 1);
260 N := High(NetClients);
261 end;
262 end;
264 if N >= 0 then
265 begin
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]));
275 end;
277 Result := N;
278 end;
280 function g_Net_Init(): Boolean;
281 var
282 F: TextFile;
283 IPstr: string;
284 IP: LongWord;
285 I: Integer;
286 begin
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);
292 NetPeer := nil;
293 NetHost := nil;
294 NetMyID := -1;
295 NetPlrUID1 := -1;
296 NetPlrUID2 := -1;
297 NetAddr.port := 25666;
298 SetLength(NetBannedHosts, 0);
299 if FileExists(DataDir + BANLIST_FILENAME) then
300 begin
301 Assign(F, DataDir + BANLIST_FILENAME);
302 Reset(F);
303 while not EOF(F) do
304 begin
305 Readln(F, IPstr);
306 if StrToIp(IPstr, IP) then
307 g_Net_BanHost(IP);
308 end;
309 CloseFile(F);
310 g_Net_SaveBanList();
311 end;
313 Result := (enet_initialize() = 0);
314 end;
316 procedure g_Net_Flush();
317 begin
318 if NetMode = NET_SERVER then
319 g_Net_Host_FlushBuffers()
320 else
321 g_Net_Client_FlushBuffers();
322 enet_host_flush(NetHost);
323 end;
325 procedure g_Net_Cleanup();
326 var
327 I: Integer;
328 begin
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);
335 NetClientCount := 0;
337 NetPeer := nil;
338 NetHost := nil;
339 NetMPeer := nil;
340 NetMHost := nil;
341 NetMyID := -1;
342 NetPlrUID1 := -1;
343 NetPlrUID2 := -1;
344 NetState := NET_STATE_NONE;
346 NetPongSock := ENET_SOCKET_NULL;
348 NetTimeToMaster := 0;
349 NetTimeToUpdate := 0;
350 NetTimeToReliable := 0;
352 NetMode := NET_NONE;
354 if NetDump then
355 g_Net_DumpEnd();
356 end;
358 procedure g_Net_Free();
359 begin
360 g_Net_Cleanup();
362 enet_deinitialize();
363 NetInitDone := False;
364 end;
367 { /// SERVER FUNCTIONS /// }
370 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
371 begin
372 if NetMode <> NET_NONE then
373 begin
374 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
375 Result := False;
376 Exit;
377 end;
379 Result := True;
381 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
382 if not NetInitDone then
383 begin
384 if (not g_Net_Init()) then
385 begin
386 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
387 Result := False;
388 Exit;
389 end
390 else
391 NetInitDone := True;
392 end;
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
400 begin
401 g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
402 Result := False;
403 g_Net_Cleanup;
404 Exit;
405 end;
407 NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
408 if NetPongSock <> ENET_SOCKET_NULL then
409 begin
410 NetPongAddr.host := IPAddr;
411 NetPongAddr.port := Port + 1;
412 if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
413 begin
414 enet_socket_destroy(NetPongSock);
415 NetPongSock := ENET_SOCKET_NULL;
416 end
417 else
418 enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
419 end;
421 NetMode := NET_SERVER;
422 e_Buffer_Clear(@NetOut);
424 if NetDump then
425 g_Net_DumpStart();
426 end;
428 procedure g_Net_Host_Die();
429 var
430 I: Integer;
431 begin
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
445 begin
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;
451 end;
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);
460 NetMode := NET_NONE;
462 g_Net_Cleanup;
463 e_WriteLog('NET: Server stopped', MSG_NOTIFY);
464 end;
466 procedure g_Net_Host_FlushBuffers();
467 var
468 I: Integer;
469 begin
470 // send broadcast
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
477 begin
478 SendBuffer(@SendBuf[CH_RELIABLE], CH_RELIABLE, Peer);
479 SendBuffer(@SendBuf[CH_UNRELIABLE], CH_UNRELIABLE, Peer);
480 end;
481 end;
483 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
484 var
485 I: Integer;
486 B: pTBuffer;
487 begin
488 if (Reliable) then
489 I := CH_RELIABLE
490 else
491 I := CH_UNRELIABLE;
493 if (ID >= 0) then
494 begin
495 if (ID > High(NetClients)) or (NetClients[ID].Peer = nil) then
496 begin
497 e_Buffer_Clear(@NetOut);
498 Exit;
499 end;
500 B := Addr(NetClients[ID].SendBuf[I]);
501 end
502 else
503 begin
504 B := Addr(NetSend[I]);
505 end;
507 e_Buffer_Write(B, @NetOut);
508 e_Buffer_Clear(@NetOut);
509 end;
511 procedure g_Net_Host_CheckPings();
512 var
513 ClAddr: ENetAddress;
514 Buf: ENetBuffer;
515 Len: Integer;
516 ClTime: Int64;
517 Ping: array [0..9] of Byte;
518 NPl: Byte;
519 begin
520 if NetPongSock = ENET_SOCKET_NULL then Exit;
522 Buf.data := Addr(Ping[0]);
523 Buf.dataLength := 2+8;
525 Ping[0] := 0;
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
531 begin
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();
539 NPl := 0;
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);
550 end;
551 end;
553 function g_Net_Host_Update(): enet_size_t;
554 var
555 IP: string;
556 Port: Word;
557 ID, I: Integer;
558 TC: pTNetClient;
559 TP: TPlayer;
560 begin
561 IP := '';
562 Result := 0;
564 if NetUseMaster then
565 begin
566 g_Net_Slist_Check;
567 g_Net_Host_CheckPings;
568 end;
570 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
571 begin
572 case (NetEvent.kind) of
573 ENET_EVENT_TYPE_CONNECT:
574 begin
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
581 begin
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);
588 Exit;
589 end;
591 ID := g_Net_FindSlot();
593 if ID < 0 then
594 begin
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);
601 Exit;
602 end;
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);
612 Inc(NetClientCount);
613 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
614 end;
616 ENET_EVENT_TYPE_RECEIVE:
617 begin
618 ID := Byte(NetEvent.peer^.data^);
619 if ID > High(NetClients) then Exit;
620 TC := @NetClients[ID];
621 if NetDump then
622 g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
623 g_Net_HostMsgHandler(TC, NetEvent.packet);
624 end;
626 ENET_EVENT_TYPE_DISCONNECT:
627 begin
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);
637 if TP <> nil then
638 begin
639 TP.Lives := 0;
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);
644 end;
646 TC^.Used := False;
647 TC^.State := NET_STATE_NONE;
648 TC^.Peer := nil;
649 TC^.Player := 0;
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]));
655 Dec(NetClientCount);
657 if NetUseMaster then g_Net_Slist_Update;
658 end;
659 end;
660 end;
661 end;
664 { /// CLIENT FUNCTIONS /// }
667 procedure g_Net_Disconnect(Forced: Boolean = False);
668 begin
669 if NetMode <> NET_CLIENT then Exit;
670 if (NetHost = nil) or (NetPeer = nil) then Exit;
672 if not Forced then
673 begin
674 enet_peer_disconnect(NetPeer, NET_DISC_NONE);
676 while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
677 begin
678 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
679 begin
680 NetPeer := nil;
681 break;
682 end;
684 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
685 enet_packet_destroy(NetEvent.packet);
686 end;
688 if NetPeer <> nil then
689 begin
690 enet_peer_reset(NetPeer);
691 NetPeer := nil;
692 end;
693 end
694 else
695 begin
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);
700 end;
702 if NetHost <> nil then
703 begin
704 enet_host_destroy(NetHost);
705 NetHost := nil;
706 end;
707 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
709 g_Net_Cleanup;
710 e_WriteLog('NET: Disconnected', MSG_NOTIFY);
711 end;
713 procedure g_Net_Client_FlushBuffers();
714 begin
715 SendBuffer(@NetSend[CH_RELIABLE], CH_RELIABLE, NetPeer);
716 SendBuffer(@NetSend[CH_UNRELIABLE], CH_UNRELIABLE, NetPeer);
717 end;
719 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
720 var
721 I: Integer;
722 begin
723 if (Reliable) then
724 I := CH_RELIABLE
725 else
726 I := CH_UNRELIABLE;
727 e_Buffer_Write(@NetSend[I], @NetOut);
728 e_Buffer_Clear(@NetOut);
729 end;
731 function g_Net_Client_Update(): enet_size_t;
732 begin
733 Result := 0;
734 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
735 begin
736 case NetEvent.kind of
737 ENET_EVENT_TYPE_RECEIVE:
738 begin
739 if NetDump then
740 g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
741 g_Net_ClientMsgHandler(NetEvent.packet);
742 end;
744 ENET_EVENT_TYPE_DISCONNECT:
745 begin
746 g_Net_Disconnect(True);
747 Result := 1;
748 Exit;
749 end;
750 end;
751 end
752 end;
754 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
755 begin
756 Result := 0;
757 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
758 begin
759 case NetEvent.kind of
760 ENET_EVENT_TYPE_RECEIVE:
761 begin
762 if NetDump then
763 g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
764 g_Net_ClientLightMsgHandler(NetEvent.packet);
765 end;
767 ENET_EVENT_TYPE_DISCONNECT:
768 begin
769 g_Net_Disconnect(True);
770 Result := 1;
771 Exit;
772 end;
773 end;
774 end;
775 end;
777 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
778 var
779 OuterLoop: Boolean;
780 begin
781 if NetMode <> NET_NONE then
782 begin
783 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
784 Result := False;
785 Exit;
786 end;
788 Result := True;
790 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
791 [IP, Port]));
792 if not NetInitDone then
793 begin
794 if (not g_Net_Init()) then
795 begin
796 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
797 Result := False;
798 Exit;
799 end
800 else
801 NetInitDone := True;
802 end;
804 NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
806 if (NetHost = nil) then
807 begin
808 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
809 g_Net_Cleanup;
810 Result := False;
811 Exit;
812 end;
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
820 begin
821 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
822 enet_host_destroy(NetHost);
823 g_Net_Cleanup;
824 Result := False;
825 Exit;
826 end;
828 OuterLoop := True;
829 while OuterLoop do
830 begin
831 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
832 begin
833 if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
834 begin
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);
839 NetClientIP := IP;
840 NetClientPort := Port;
841 if NetDump then
842 g_Net_DumpStart();
843 Exit;
844 end;
845 end;
847 ProcessLoading();
849 e_PollInput();
851 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
852 OuterLoop := False;
853 end;
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
858 begin
859 enet_host_destroy(NetHost);
860 NetHost := nil;
861 end;
862 g_Net_Cleanup();
863 Result := False;
864 end;
866 function IpToStr(IP: LongWord): string;
867 var
868 Ptr: Pointer;
869 begin
870 Result := '';
871 Ptr := Addr(IP);
873 e_Raw_Seek(0);
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));
878 e_Raw_Seek(0);
879 end;
881 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
882 var
883 EAddr: ENetAddress;
884 begin
885 Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
886 IP := EAddr.host;
887 end;
889 function g_Net_Client_ByName(Name: string): pTNetClient;
890 var
891 a: Integer;
892 pl: TPlayer;
893 begin
894 Result := nil;
895 for a := Low(NetClients) to High(NetClients) do
896 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
897 begin
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
902 begin
903 Result := @NetClients[a];
904 Exit;
905 end;
906 end;
907 end;
909 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
910 var
911 a: Integer;
912 begin
913 Result := nil;
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
917 begin
918 Result := @NetClients[a];
919 Exit;
920 end;
921 end;
923 function g_Net_ClientName_ByID(ID: Integer): string;
924 var
925 a: Integer;
926 pl: TPlayer;
927 begin
928 Result := '';
929 if ID = NET_EVERYONE then
930 Exit;
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
933 begin
934 pl := g_Player_Get(NetClients[a].Player);
935 if pl = nil then Exit;
936 Result := pl.Name;
937 end;
938 end;
940 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
941 var
942 P: pENetPacket;
943 F: enet_uint32;
944 dataLength: Cardinal;
945 begin
946 dataLength := Length(Data);
948 if (Reliable) then
949 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
950 else
951 F := 0;
953 if (peer <> nil) then
954 begin
955 P := enet_packet_create(@Data[0], dataLength, F);
956 if not Assigned(P) then Exit;
957 enet_peer_send(peer, CH_DOWNLOAD, P);
958 end
959 else
960 begin
961 P := enet_packet_create(@Data[0], dataLength, F);
962 if not Assigned(P) then Exit;
963 enet_host_broadcast(NetHost, CH_DOWNLOAD, P);
964 end;
966 enet_host_flush(NetHost);
967 end;
969 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
970 var
971 downloadEvent: ENetEvent;
972 OuterLoop: Boolean;
973 MID: Byte;
974 Ptr: Pointer;
975 Len: LongWord;
976 msgStream: TMemoryStream;
977 begin
978 FillChar(downloadEvent, SizeOf(downloadEvent), 0);
979 msgStream := nil;
980 OuterLoop := True;
981 while OuterLoop do
982 begin
983 while (enet_host_service(NetHost, @downloadEvent, 0) > 0) do
984 begin
985 if (downloadEvent.kind = ENET_EVENT_TYPE_RECEIVE) and (downloadEvent.packet^.dataLength > 2) then
986 begin
987 Len := PWord(downloadEvent.packet^.data)^;
988 if Len = 0 then break;
989 Ptr := downloadEvent.packet^.data + 2; // skip length
990 MID := Byte(Ptr^);
992 if (MID = msgId) then
993 begin
994 msgStream := TMemoryStream.Create;
995 msgStream.SetSize(downloadEvent.packet^.dataLength - 2);
996 msgStream.WriteBuffer(Ptr^, downloadEvent.packet^.dataLength - 2);
997 msgStream.Seek(0, soFromBeginning);
999 OuterLoop := False;
1000 enet_packet_destroy(downloadEvent.packet);
1001 break;
1002 end
1003 else begin
1004 enet_packet_destroy(downloadEvent.packet);
1005 end;
1006 end
1007 else
1008 if (downloadEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
1009 begin
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);
1013 OuterLoop := False;
1014 Break;
1015 end;
1016 end;
1018 ProcessLoading();
1020 e_PollInput();
1022 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
1023 break;
1024 end;
1025 Result := msgStream;
1026 end;
1028 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
1029 var
1030 I: Integer;
1031 begin
1032 Result := False;
1033 if NetBannedHosts = nil then
1034 Exit;
1035 for I := 0 to High(NetBannedHosts) do
1036 if (NetBannedHosts[I].IP = IP) and ((not Perm) or (NetBannedHosts[I].Perm)) then
1037 begin
1038 Result := True;
1039 break;
1040 end;
1041 end;
1043 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
1044 var
1045 I, P: Integer;
1046 begin
1047 if IP = 0 then
1048 Exit;
1049 if g_Net_IsHostBanned(IP, Perm) then
1050 Exit;
1052 P := -1;
1053 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
1054 if NetBannedHosts[I].IP = 0 then
1055 begin
1056 P := I;
1057 break;
1058 end;
1060 if P < 0 then
1061 begin
1062 SetLength(NetBannedHosts, Length(NetBannedHosts) + 1);
1063 P := High(NetBannedHosts);
1064 end;
1066 NetBannedHosts[P].IP := IP;
1067 NetBannedHosts[P].Perm := Perm;
1068 end;
1070 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
1071 var
1072 a: LongWord;
1073 b: Boolean;
1074 begin
1075 b := StrToIp(IP, a);
1076 if b then
1077 g_Net_BanHost(a, Perm);
1078 end;
1080 procedure g_Net_UnbanNonPermHosts();
1081 var
1082 I: Integer;
1083 begin
1084 if NetBannedHosts = nil then
1085 Exit;
1086 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
1087 if (NetBannedHosts[I].IP > 0) and not NetBannedHosts[I].Perm then
1088 begin
1089 NetBannedHosts[I].IP := 0;
1090 NetBannedHosts[I].Perm := True;
1091 end;
1092 end;
1094 function g_Net_UnbanHost(IP: string): Boolean; overload;
1095 var
1096 a: LongWord;
1097 begin
1098 Result := StrToIp(IP, a);
1099 if Result then
1100 Result := g_Net_UnbanHost(a);
1101 end;
1103 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
1104 var
1105 I: Integer;
1106 begin
1107 Result := False;
1108 if IP = 0 then
1109 Exit;
1110 if NetBannedHosts = nil then
1111 Exit;
1112 for I := 0 to High(NetBannedHosts) do
1113 if NetBannedHosts[I].IP = IP then
1114 begin
1115 NetBannedHosts[I].IP := 0;
1116 NetBannedHosts[I].Perm := True;
1117 Result := True;
1118 // no break here to clear all bans of this host, perm and non-perm
1119 end;
1120 end;
1122 procedure g_Net_SaveBanList();
1123 var
1124 F: TextFile;
1125 I: Integer;
1126 begin
1127 Assign(F, DataDir + BANLIST_FILENAME);
1128 Rewrite(F);
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));
1133 CloseFile(F);
1134 end;
1136 procedure g_Net_DumpStart();
1137 begin
1138 if NetMode = NET_SERVER then
1139 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_server')
1140 else
1141 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_client');
1142 end;
1144 procedure g_Net_DumpSendBuffer(Buf: pTBuffer);
1145 begin
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);
1154 end;
1156 procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord);
1157 begin
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);
1167 end;
1169 procedure g_Net_DumpEnd();
1170 begin
1171 NetDumpFile.Free();
1172 NetDumpFile := nil;
1173 end;
1175 end.