DEADSOFTWARE

warnings for -O3
[d2df-sdl.git] / src / game / g_net.pas
1 unit g_net;
3 interface
5 uses
6 e_log, e_fixedbuffer, ENet, ENetTypes, ENetPlatform, Classes;
8 const
9 NET_PROTOCOL_VER = 164;
11 NET_MAXCLIENTS = 24;
12 NET_CHANS = 11;
14 NET_CHAN_SERVICE = 0;
15 NET_CHAN_IMPORTANT = 1;
16 NET_CHAN_GAME = 2;
17 NET_CHAN_PLAYER = 3;
18 NET_CHAN_PLAYERPOS = 4;
19 NET_CHAN_MONSTER = 5;
20 NET_CHAN_MONSTERPOS = 6;
21 NET_CHAN_LARGEDATA = 7;
22 NET_CHAN_CHAT = 8;
23 NET_CHAN_DOWNLOAD = 9;
24 NET_CHAN_SHOTS = 10;
26 NET_NONE = 0;
27 NET_SERVER = 1;
28 NET_CLIENT = 2;
30 NET_BUFSIZE = 65536;
32 NET_EVERYONE = -1;
34 NET_DISC_NONE: enet_uint32 = 0;
35 NET_DISC_PROTOCOL: enet_uint32 = 1;
36 NET_DISC_VERSION: enet_uint32 = 2;
37 NET_DISC_FULL: enet_uint32 = 3;
38 NET_DISC_KICK: enet_uint32 = 4;
39 NET_DISC_DOWN: enet_uint32 = 5;
40 NET_DISC_PASSWORD: enet_uint32 = 6;
41 NET_DISC_TEMPBAN: enet_uint32 = 7;
42 NET_DISC_BAN: enet_uint32 = 8;
43 NET_DISC_MAX: enet_uint32 = 8;
45 NET_STATE_NONE = 0;
46 NET_STATE_AUTH = 1;
47 NET_STATE_GAME = 2;
49 BANLIST_FILENAME = 'banlist.txt';
51 type
52 TNetClient = record
53 ID: Byte;
54 Used: Boolean;
55 State: Byte;
56 Peer: pENetPeer;
57 Player: Word;
58 RequestedFullUpdate: Boolean;
59 RCONAuth: Boolean;
60 Voted: Boolean;
61 end;
62 TBanRecord = record
63 IP: LongWord;
64 Perm: Boolean;
65 end;
66 pTNetClient = ^TNetClient;
68 AByte = array of Byte;
70 var
71 NetInitDone: Boolean = False;
72 NetMode: Byte = NET_NONE;
74 NetServerName: string = 'Unnamed Server';
75 NetPassword: string = '';
76 NetPort: Word = 25666;
78 NetAllowRCON: Boolean = False;
79 NetRCONPassword: string = '';
81 NetTimeToUpdate: Cardinal = 0;
82 NetTimeToReliable: Cardinal = 0;
83 NetTimeToMaster: Cardinal = 0;
85 NetHost: pENetHost = nil;
86 NetPeer: pENetPeer = nil;
87 NetEvent: ENetEvent;
88 NetAddr: ENetAddress;
90 NetPongAddr: ENetAddress;
91 NetPongSock: ENetSocket = ENET_SOCKET_NULL;
93 NetUseMaster: Boolean = True;
94 NetSlistAddr: ENetAddress;
95 NetSlistIP: string = 'mpms.doom2d.org';
96 NetSlistPort: Word = 25665;
98 NetClientIP: string = '127.0.0.1';
99 NetClientPort: Word = 25666;
101 NetIn, NetOut: TBuffer;
103 NetClients: array of TNetClient;
104 NetClientCount: Byte = 0;
105 NetMaxClients: Byte = 255;
106 NetBannedHosts: array of TBanRecord;
108 NetState: Integer = NET_STATE_NONE;
110 NetMyID: Integer = -1;
111 NetPlrUID1: Integer = -1;
112 NetPlrUID2: Integer = -1;
114 NetInterpLevel: Integer = 1;
115 NetUpdateRate: Cardinal = 0; // as soon as possible
116 NetRelupdRate: Cardinal = 18; // around two times a second
117 NetMasterRate: Cardinal = 60000;
119 NetForcePlayerUpdate: Boolean = False;
120 NetPredictSelf: Boolean = True;
121 NetGotKeys: Boolean = False;
123 NetGotEverything: Boolean = False;
125 function g_Net_Init(): Boolean;
126 procedure g_Net_Cleanup();
127 procedure g_Net_Free();
128 procedure g_Net_Flush();
130 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
131 procedure g_Net_Host_Die();
132 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
133 function g_Net_Host_Update(): enet_size_t;
135 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
136 procedure g_Net_Disconnect(Forced: Boolean = False);
137 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
138 function g_Net_Client_Update(): enet_size_t;
139 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
141 function g_Net_Client_ByName(Name: string): pTNetClient;
142 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
143 function g_Net_ClientName_ByID(ID: Integer): string;
145 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
146 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
148 function IpToStr(IP: LongWord): string;
149 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
151 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
152 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
153 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
154 function g_Net_UnbanHost(IP: string): Boolean; overload;
155 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
156 procedure g_Net_UnbanNonPermHosts();
157 procedure g_Net_SaveBanList();
159 implementation
161 uses
162 SysUtils,
163 e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
164 g_main, g_game, g_language, g_weapons;
167 { /// SERVICE FUNCTIONS /// }
170 function g_Net_FindSlot(): Integer;
171 var
172 I: Integer;
173 F: Boolean;
174 N, C: Integer;
175 begin
176 N := -1;
177 F := False;
178 C := 0;
179 for I := Low(NetClients) to High(NetClients) do
180 begin
181 if NetClients[I].Used then
182 Inc(C)
183 else
184 if not F then
185 begin
186 F := True;
187 N := I;
188 end;
189 end;
190 if C >= NetMaxClients then
191 begin
192 Result := -1;
193 Exit;
194 end;
196 if not F then
197 begin
198 if (Length(NetClients) >= NetMaxClients) then
199 N := -1
200 else
201 begin
202 SetLength(NetClients, Length(NetClients) + 1);
203 N := High(NetClients);
204 end;
205 end;
207 if N >= 0 then
208 begin
209 NetClients[N].Used := True;
210 NetClients[N].ID := N;
211 NetClients[N].RequestedFullUpdate := False;
212 NetClients[N].RCONAuth := False;
213 NetClients[N].Voted := False;
214 NetClients[N].Player := 0;
215 end;
217 Result := N;
218 end;
220 function g_Net_Init(): Boolean;
221 var
222 F: TextFile;
223 IPstr: string;
224 IP: LongWord;
225 begin
226 e_Buffer_Clear(@NetIn);
227 e_Buffer_Clear(@NetOut);
228 SetLength(NetClients, 0);
229 NetPeer := nil;
230 NetHost := nil;
231 NetMyID := -1;
232 NetPlrUID1 := -1;
233 NetPlrUID2 := -1;
234 NetAddr.port := 25666;
235 SetLength(NetBannedHosts, 0);
236 if FileExists(DataDir + BANLIST_FILENAME) then
237 begin
238 Assign(F, DataDir + BANLIST_FILENAME);
239 Reset(F);
240 while not EOF(F) do
241 begin
242 Readln(F, IPstr);
243 if StrToIp(IPstr, IP) then
244 g_Net_BanHost(IP);
245 end;
246 CloseFile(F);
247 g_Net_SaveBanList();
248 end;
250 Result := (enet_initialize() = 0);
251 end;
253 procedure g_Net_Flush();
254 begin
255 enet_host_flush(NetHost);
256 end;
258 procedure g_Net_Cleanup();
259 begin
260 e_Buffer_Clear(@NetIn);
261 e_Buffer_Clear(@NetOut);
263 SetLength(NetClients, 0);
264 NetClientCount := 0;
266 NetPeer := nil;
267 NetHost := nil;
268 NetMPeer := nil;
269 NetMHost := nil;
270 NetMyID := -1;
271 NetPlrUID1 := -1;
272 NetPlrUID2 := -1;
273 NetState := NET_STATE_NONE;
275 NetPongSock := ENET_SOCKET_NULL;
277 NetTimeToMaster := 0;
278 NetTimeToUpdate := 0;
279 NetTimeToReliable := 0;
281 NetMode := NET_NONE;
282 end;
284 procedure g_Net_Free();
285 begin
286 g_Net_Cleanup();
288 enet_deinitialize();
289 NetInitDone := False;
290 end;
293 { /// SERVER FUNCTIONS /// }
296 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
297 begin
298 if NetMode <> NET_NONE then
299 begin
300 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
301 Result := False;
302 Exit;
303 end;
305 Result := True;
307 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
308 if not NetInitDone then
309 begin
310 if (not g_Net_Init()) then
311 begin
312 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
313 Result := False;
314 Exit;
315 end
316 else
317 NetInitDone := True;
318 end;
320 NetAddr.host := IPAddr;
321 NetAddr.port := Port;
323 NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
325 if (NetHost = nil) then
326 begin
327 g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
328 Result := False;
329 g_Net_Cleanup;
330 Exit;
331 end;
333 NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
334 if NetPongSock <> ENET_SOCKET_NULL then
335 begin
336 NetPongAddr.host := IPAddr;
337 NetPongAddr.port := Port + 1;
338 if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
339 begin
340 enet_socket_destroy(NetPongSock);
341 NetPongSock := ENET_SOCKET_NULL;
342 end
343 else
344 enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
345 end;
347 NetMode := NET_SERVER;
348 e_Buffer_Clear(@NetOut);
349 end;
351 procedure g_Net_Host_Die();
352 var
353 I: Integer;
354 begin
355 if NetMode <> NET_SERVER then Exit;
357 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
358 for I := 0 to High(NetClients) do
359 if NetClients[I].Used then
360 enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
362 while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
363 if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
364 enet_packet_destroy(NetEvent.packet);
366 for I := 0 to High(NetClients) do
367 if NetClients[I].Used then
368 begin
369 FreeMemory(NetClients[I].Peer^.data);
370 NetClients[I].Peer^.data := nil;
371 enet_peer_reset(NetClients[I].Peer);
372 NetClients[I].Peer := nil;
373 NetClients[I].Used := False;
374 end;
376 if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
377 if NetPongSock <> ENET_SOCKET_NULL then
378 enet_socket_destroy(NetPongSock);
380 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
381 enet_host_destroy(NetHost);
383 NetMode := NET_NONE;
385 g_Net_Cleanup;
386 e_WriteLog('NET: Server stopped', MSG_NOTIFY);
387 end;
390 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
391 var
392 P: pENetPacket;
393 F: enet_uint32;
394 begin
395 if (Reliable) then
396 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
397 else
398 F := 0;
400 if (ID >= 0) then
401 begin
402 if ID > High(NetClients) then Exit;
403 if NetClients[ID].Peer = nil then Exit;
405 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
406 if not Assigned(P) then Exit;
408 enet_peer_send(NetClients[ID].Peer, Chan, P);
409 end
410 else
411 begin
412 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
413 if not Assigned(P) then Exit;
415 enet_host_widecast(NetHost, Chan, P);
416 end;
418 g_Net_Flush();
419 e_Buffer_Clear(@NetOut);
420 end;
422 procedure g_Net_Host_CheckPings();
423 var
424 ClAddr: ENetAddress;
425 Buf: ENetBuffer;
426 Len, ClTime: Integer;
427 Ping: array [0..5] of Byte;
428 NPl: Byte;
429 begin
430 if NetPongSock = ENET_SOCKET_NULL then Exit;
432 Buf.data := Addr(Ping[0]);
433 Buf.dataLength := 6;
435 Ping[0] := 0;
437 Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
438 if Len < 0 then Exit;
440 if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
441 begin
442 ClTime := Integer(Addr(Ping[2])^);
444 e_Buffer_Clear(@NetOut);
445 e_Buffer_Write(@NetOut, Byte(Ord('D')));
446 e_Buffer_Write(@NetOut, Byte(Ord('F')));
447 e_Buffer_Write(@NetOut, ClTime);
448 g_Net_Slist_WriteInfo();
449 NPl := 0;
450 if gPlayer1 <> nil then Inc(NPl);
451 if gPlayer2 <> nil then Inc(NPl);
452 e_Buffer_Write(@NetOut, NPl);
453 e_Buffer_Write(@NetOut, gNumBots);
455 Buf.data := Addr(NetOut.Data[0]);
456 Buf.dataLength := NetOut.WritePos;
457 enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
459 e_Buffer_Clear(@NetOut);
460 end;
461 end;
463 function g_Net_Host_Update(): enet_size_t;
464 var
465 IP: string;
466 Port: Word;
467 ID: Integer;
468 TC: pTNetClient;
469 TP: TPlayer;
470 begin
471 IP := '';
472 Result := 0;
474 if NetUseMaster then
475 begin
476 g_Net_Slist_Check;
477 g_Net_Host_CheckPings;
478 end;
480 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
481 begin
482 case (NetEvent.kind) of
483 ENET_EVENT_TYPE_CONNECT:
484 begin
485 IP := IpToStr(NetEvent.Peer^.address.host);
486 Port := NetEvent.Peer^.address.port;
487 g_Console_Add(_lc[I_NET_MSG] +
488 Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
490 if (NetEvent.data <> NET_PROTOCOL_VER) then
491 begin
492 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
493 _lc[I_NET_DISC_PROTOCOL]);
494 NetEvent.peer^.data := GetMemory(SizeOf(Byte));
495 Byte(NetEvent.peer^.data^) := 255;
496 enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
497 enet_host_flush(NetHost);
498 Exit;
499 end;
501 ID := g_Net_FindSlot();
503 if ID < 0 then
504 begin
505 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
506 _lc[I_NET_DISC_FULL]);
507 NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
508 Byte(NetEvent.peer^.data^) := 255;
509 enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
510 enet_host_flush(NetHost);
511 Exit;
512 end;
514 NetClients[ID].Peer := NetEvent.peer;
515 NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
516 Byte(NetClients[ID].Peer^.data^) := ID;
517 NetClients[ID].State := NET_STATE_AUTH;
518 NetClients[ID].RCONAuth := False;
520 enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
522 Inc(NetClientCount);
523 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
524 end;
526 ENET_EVENT_TYPE_RECEIVE:
527 begin
528 ID := Byte(NetEvent.peer^.data^);
529 if ID > High(NetClients) then Exit;
530 TC := @NetClients[ID];
532 g_Net_HostMsgHandler(TC, NetEvent.packet);
533 end;
535 ENET_EVENT_TYPE_DISCONNECT:
536 begin
537 ID := Byte(NetEvent.peer^.data^);
538 if ID > High(NetClients) then Exit;
539 TC := @NetClients[ID];
540 if TC = nil then Exit;
542 if not (TC^.Used) then Exit;
544 TP := g_Player_Get(TC^.Player);
546 if TP <> nil then
547 begin
548 TP.Lives := 0;
549 TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
550 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
551 e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', MSG_NOTIFY);
552 g_Player_Remove(TP.UID);
553 end;
555 TC^.Used := False;
556 TC^.State := NET_STATE_NONE;
557 TC^.Peer := nil;
558 TC^.Player := 0;
559 TC^.RequestedFullUpdate := False;
561 FreeMemory(NetEvent.peer^.data);
562 NetEvent.peer^.data := nil;
563 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
564 Dec(NetClientCount);
566 if NetUseMaster then g_Net_Slist_Update;
567 end;
568 end;
569 end;
570 end;
573 { /// CLIENT FUNCTIONS /// }
576 procedure g_Net_Disconnect(Forced: Boolean = False);
577 begin
578 if NetMode <> NET_CLIENT then Exit;
579 if (NetHost = nil) or (NetPeer = nil) then Exit;
581 if not Forced then
582 begin
583 enet_peer_disconnect(NetPeer, NET_DISC_NONE);
585 while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
586 begin
587 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
588 begin
589 NetPeer := nil;
590 break;
591 end;
593 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
594 enet_packet_destroy(NetEvent.packet);
595 end;
597 if NetPeer <> nil then
598 begin
599 enet_peer_reset(NetPeer);
600 NetPeer := nil;
601 end;
602 end
603 else
604 begin
605 e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), MSG_NOTIFY);
606 if (NetEvent.data <= NET_DISC_MAX) then
607 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
608 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
609 end;
611 if NetHost <> nil then
612 begin
613 enet_host_destroy(NetHost);
614 NetHost := nil;
615 end;
616 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
618 g_Net_Cleanup;
619 e_WriteLog('NET: Disconnected', MSG_NOTIFY);
620 end;
622 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
623 var
624 P: pENetPacket;
625 F: enet_uint32;
626 begin
627 if (Reliable) then
628 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
629 else
630 F := 0;
632 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
633 if not Assigned(P) then Exit;
635 enet_peer_send(NetPeer, Chan, P);
636 g_Net_Flush();
637 e_Buffer_Clear(@NetOut);
638 end;
640 function g_Net_Client_Update(): enet_size_t;
641 begin
642 Result := 0;
643 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
644 begin
645 case NetEvent.kind of
646 ENET_EVENT_TYPE_RECEIVE:
647 g_Net_ClientMsgHandler(NetEvent.packet);
649 ENET_EVENT_TYPE_DISCONNECT:
650 begin
651 g_Net_Disconnect(True);
652 Result := 1;
653 Exit;
654 end;
655 end;
656 end
657 end;
659 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
660 begin
661 Result := 0;
662 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
663 begin
664 case NetEvent.kind of
665 ENET_EVENT_TYPE_RECEIVE:
666 g_Net_ClientLightMsgHandler(NetEvent.packet);
668 ENET_EVENT_TYPE_DISCONNECT:
669 begin
670 g_Net_Disconnect(True);
671 Result := 1;
672 Exit;
673 end;
674 end;
675 end;
676 g_Net_Flush();
677 end;
679 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
680 var
681 OuterLoop: Boolean;
682 begin
683 if NetMode <> NET_NONE then
684 begin
685 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
686 Result := False;
687 Exit;
688 end;
690 Result := True;
692 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
693 [IP, Port]));
694 if not NetInitDone then
695 begin
696 if (not g_Net_Init()) then
697 begin
698 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
699 Result := False;
700 Exit;
701 end
702 else
703 NetInitDone := True;
704 end;
706 NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
708 if (NetHost = nil) then
709 begin
710 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
711 g_Net_Cleanup;
712 Result := False;
713 Exit;
714 end;
716 enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
717 NetAddr.port := Port;
719 NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
721 if (NetPeer = nil) then
722 begin
723 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
724 enet_host_destroy(NetHost);
725 g_Net_Cleanup;
726 Result := False;
727 Exit;
728 end;
730 OuterLoop := True;
731 while OuterLoop do
732 begin
733 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
734 begin
735 if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
736 begin
737 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
738 NetMode := NET_CLIENT;
739 e_Buffer_Clear(@NetOut);
740 enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
741 NetClientIP := IP;
742 NetClientPort := Port;
743 Exit;
744 end;
745 end;
747 ProcessLoading();
749 e_PollInput();
751 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
752 OuterLoop := False;
753 end;
755 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
756 if NetPeer <> nil then enet_peer_reset(NetPeer);
757 if NetHost <> nil then
758 begin
759 enet_host_destroy(NetHost);
760 NetHost := nil;
761 end;
762 g_Net_Cleanup();
763 Result := False;
764 end;
766 function IpToStr(IP: LongWord): string;
767 var
768 Ptr: Pointer;
769 begin
770 Result := '';
771 Ptr := Addr(IP);
773 e_Raw_Seek(0);
774 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
775 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
776 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
777 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr));
778 e_Raw_Seek(0);
779 end;
781 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
782 var
783 EAddr: ENetAddress;
784 begin
785 Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
786 IP := EAddr.host;
787 end;
789 function g_Net_Client_ByName(Name: string): pTNetClient;
790 var
791 a: Integer;
792 pl: TPlayer;
793 begin
794 Result := nil;
795 for a := Low(NetClients) to High(NetClients) do
796 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
797 begin
798 pl := g_Player_Get(NetClients[a].Player);
799 if pl = nil then continue;
800 if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
801 if NetClients[a].Peer <> nil then
802 begin
803 Result := @NetClients[a];
804 Exit;
805 end;
806 end;
807 end;
809 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
810 var
811 a: Integer;
812 begin
813 Result := nil;
814 for a := Low(NetClients) to High(NetClients) do
815 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
816 if NetClients[a].Player = PID then
817 begin
818 Result := @NetClients[a];
819 Exit;
820 end;
821 end;
823 function g_Net_ClientName_ByID(ID: Integer): string;
824 var
825 a: Integer;
826 pl: TPlayer;
827 begin
828 Result := '';
829 if ID = NET_EVERYONE then
830 Exit;
831 for a := Low(NetClients) to High(NetClients) do
832 if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
833 begin
834 pl := g_Player_Get(NetClients[a].Player);
835 if pl = nil then Exit;
836 Result := pl.Name;
837 end;
838 end;
840 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
841 var
842 P: pENetPacket;
843 F: enet_uint32;
844 dataLength: Cardinal;
845 begin
846 dataLength := Length(Data);
848 if (Reliable) then
849 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
850 else
851 F := 0;
853 if (peer <> nil) then
854 begin
855 P := enet_packet_create(@Data[0], dataLength, F);
856 if not Assigned(P) then Exit;
857 enet_peer_send(peer, Chan, P);
858 end
859 else
860 begin
861 P := enet_packet_create(@Data[0], dataLength, F);
862 if not Assigned(P) then Exit;
863 enet_host_widecast(NetHost, Chan, P);
864 end;
866 enet_host_flush(NetHost);
867 end;
869 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
870 var
871 downloadEvent: ENetEvent;
872 OuterLoop: Boolean;
873 MID: Byte;
874 Ptr: Pointer;
875 msgStream: TMemoryStream;
876 begin
877 FillChar(downloadEvent, SizeOf(downloadEvent), 0);
878 msgStream := nil;
879 OuterLoop := True;
880 while OuterLoop do
881 begin
882 while (enet_host_service(NetHost, @downloadEvent, 0) > 0) do
883 begin
884 if (downloadEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
885 begin
886 Ptr := downloadEvent.packet^.data;
888 MID := Byte(Ptr^);
890 if (MID = msgId) then
891 begin
892 msgStream := TMemoryStream.Create;
893 msgStream.SetSize(downloadEvent.packet^.dataLength);
894 msgStream.WriteBuffer(Ptr^, downloadEvent.packet^.dataLength);
895 msgStream.Seek(0, soFromBeginning);
897 OuterLoop := False;
898 enet_packet_destroy(downloadEvent.packet);
899 break;
900 end
901 else begin
902 enet_packet_destroy(downloadEvent.packet);
903 end;
904 end
905 else
906 if (downloadEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
907 begin
908 if (downloadEvent.data <= NET_DISC_MAX) then
909 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
910 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + downloadEvent.data)], True);
911 OuterLoop := False;
912 Break;
913 end;
914 end;
916 ProcessLoading();
918 e_PollInput();
920 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
921 break;
922 end;
923 Result := msgStream;
924 end;
926 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
927 var
928 I: Integer;
929 begin
930 Result := False;
931 if NetBannedHosts = nil then
932 Exit;
933 for I := 0 to High(NetBannedHosts) do
934 if (NetBannedHosts[I].IP = IP) and ((not Perm) or (NetBannedHosts[I].Perm)) then
935 begin
936 Result := True;
937 break;
938 end;
939 end;
941 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
942 var
943 I, P: Integer;
944 begin
945 if IP = 0 then
946 Exit;
947 if g_Net_IsHostBanned(IP, Perm) then
948 Exit;
950 P := -1;
951 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
952 if NetBannedHosts[I].IP = 0 then
953 begin
954 P := I;
955 break;
956 end;
958 if P < 0 then
959 begin
960 SetLength(NetBannedHosts, Length(NetBannedHosts) + 1);
961 P := High(NetBannedHosts);
962 end;
964 NetBannedHosts[P].IP := IP;
965 NetBannedHosts[P].Perm := Perm;
966 end;
968 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
969 var
970 a: LongWord;
971 b: Boolean;
972 begin
973 b := StrToIp(IP, a);
974 if b then
975 g_Net_BanHost(a, Perm);
976 end;
978 procedure g_Net_UnbanNonPermHosts();
979 var
980 I: Integer;
981 begin
982 if NetBannedHosts = nil then
983 Exit;
984 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
985 if (NetBannedHosts[I].IP > 0) and not NetBannedHosts[I].Perm then
986 begin
987 NetBannedHosts[I].IP := 0;
988 NetBannedHosts[I].Perm := True;
989 end;
990 end;
992 function g_Net_UnbanHost(IP: string): Boolean; overload;
993 var
994 a: LongWord;
995 begin
996 Result := StrToIp(IP, a);
997 if Result then
998 Result := g_Net_UnbanHost(a);
999 end;
1001 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
1002 var
1003 I: Integer;
1004 begin
1005 Result := False;
1006 if IP = 0 then
1007 Exit;
1008 if NetBannedHosts = nil then
1009 Exit;
1010 for I := 0 to High(NetBannedHosts) do
1011 if NetBannedHosts[I].IP = IP then
1012 begin
1013 NetBannedHosts[I].IP := 0;
1014 NetBannedHosts[I].Perm := True;
1015 Result := True;
1016 // no break here to clear all bans of this host, perm and non-perm
1017 end;
1018 end;
1020 procedure g_Net_SaveBanList();
1021 var
1022 F: TextFile;
1023 I: Integer;
1024 begin
1025 Assign(F, DataDir + BANLIST_FILENAME);
1026 Rewrite(F);
1027 if NetBannedHosts <> nil then
1028 for I := 0 to High(NetBannedHosts) do
1029 if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
1030 Writeln(F, IpToStr(NetBannedHosts[I].IP));
1031 CloseFile(F);
1032 end;
1034 end.