DEADSOFTWARE

put "{$MODE ...}" directive in each source file; removed trailing spaces, and convert...
[d2df-sdl.git] / src / game / g_net.pas
1 {$MODE DELPHI}
2 unit g_net;
4 interface
6 uses
7 e_log, e_fixedbuffer, ENet, ENetTypes, ENetPlatform, Classes;
9 const
10 NET_PROTOCOL_VER = 164;
12 NET_MAXCLIENTS = 24;
13 NET_CHANS = 11;
15 NET_CHAN_SERVICE = 0;
16 NET_CHAN_IMPORTANT = 1;
17 NET_CHAN_GAME = 2;
18 NET_CHAN_PLAYER = 3;
19 NET_CHAN_PLAYERPOS = 4;
20 NET_CHAN_MONSTER = 5;
21 NET_CHAN_MONSTERPOS = 6;
22 NET_CHAN_LARGEDATA = 7;
23 NET_CHAN_CHAT = 8;
24 NET_CHAN_DOWNLOAD = 9;
25 NET_CHAN_SHOTS = 10;
27 NET_NONE = 0;
28 NET_SERVER = 1;
29 NET_CLIENT = 2;
31 NET_BUFSIZE = 65536;
33 NET_EVERYONE = -1;
35 NET_DISC_NONE: enet_uint32 = 0;
36 NET_DISC_PROTOCOL: enet_uint32 = 1;
37 NET_DISC_VERSION: enet_uint32 = 2;
38 NET_DISC_FULL: enet_uint32 = 3;
39 NET_DISC_KICK: enet_uint32 = 4;
40 NET_DISC_DOWN: enet_uint32 = 5;
41 NET_DISC_PASSWORD: enet_uint32 = 6;
42 NET_DISC_TEMPBAN: enet_uint32 = 7;
43 NET_DISC_BAN: enet_uint32 = 8;
44 NET_DISC_MAX: enet_uint32 = 8;
46 NET_STATE_NONE = 0;
47 NET_STATE_AUTH = 1;
48 NET_STATE_GAME = 2;
50 BANLIST_FILENAME = 'banlist.txt';
52 type
53 TNetClient = record
54 ID: Byte;
55 Used: Boolean;
56 State: Byte;
57 Peer: pENetPeer;
58 Player: Word;
59 RequestedFullUpdate: Boolean;
60 RCONAuth: Boolean;
61 Voted: Boolean;
62 end;
63 TBanRecord = record
64 IP: LongWord;
65 Perm: Boolean;
66 end;
67 pTNetClient = ^TNetClient;
69 AByte = array of Byte;
71 var
72 NetInitDone: Boolean = False;
73 NetMode: Byte = NET_NONE;
75 NetServerName: string = 'Unnamed Server';
76 NetPassword: string = '';
77 NetPort: Word = 25666;
79 NetAllowRCON: Boolean = False;
80 NetRCONPassword: string = '';
82 NetTimeToUpdate: Cardinal = 0;
83 NetTimeToReliable: Cardinal = 0;
84 NetTimeToMaster: Cardinal = 0;
86 NetHost: pENetHost = nil;
87 NetPeer: pENetPeer = nil;
88 NetEvent: ENetEvent;
89 NetAddr: ENetAddress;
91 NetPongAddr: ENetAddress;
92 NetPongSock: ENetSocket = ENET_SOCKET_NULL;
94 NetUseMaster: Boolean = True;
95 NetSlistAddr: ENetAddress;
96 NetSlistIP: string = 'mpms.doom2d.org';
97 NetSlistPort: Word = 25665;
99 NetClientIP: string = '127.0.0.1';
100 NetClientPort: Word = 25666;
102 NetIn, NetOut: TBuffer;
104 NetClients: array of TNetClient;
105 NetClientCount: Byte = 0;
106 NetMaxClients: Byte = 255;
107 NetBannedHosts: array of TBanRecord;
109 NetState: Integer = NET_STATE_NONE;
111 NetMyID: Integer = -1;
112 NetPlrUID1: Integer = -1;
113 NetPlrUID2: Integer = -1;
115 NetInterpLevel: Integer = 1;
116 NetUpdateRate: Cardinal = 0; // as soon as possible
117 NetRelupdRate: Cardinal = 18; // around two times a second
118 NetMasterRate: Cardinal = 60000;
120 NetForcePlayerUpdate: Boolean = False;
121 NetPredictSelf: Boolean = True;
122 NetGotKeys: Boolean = False;
124 NetGotEverything: Boolean = False;
126 function g_Net_Init(): Boolean;
127 procedure g_Net_Cleanup();
128 procedure g_Net_Free();
129 procedure g_Net_Flush();
131 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
132 procedure g_Net_Host_Die();
133 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
134 function g_Net_Host_Update(): enet_size_t;
136 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
137 procedure g_Net_Disconnect(Forced: Boolean = False);
138 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
139 function g_Net_Client_Update(): enet_size_t;
140 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
142 function g_Net_Client_ByName(Name: string): pTNetClient;
143 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
144 function g_Net_ClientName_ByID(ID: Integer): string;
146 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
147 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
149 function IpToStr(IP: LongWord): string;
150 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
152 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
153 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
154 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
155 function g_Net_UnbanHost(IP: string): Boolean; overload;
156 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
157 procedure g_Net_UnbanNonPermHosts();
158 procedure g_Net_SaveBanList();
160 implementation
162 uses
163 SysUtils,
164 e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
165 g_main, g_game, g_language, g_weapons;
168 { /// SERVICE FUNCTIONS /// }
171 function g_Net_FindSlot(): Integer;
172 var
173 I: Integer;
174 F: Boolean;
175 N, C: Integer;
176 begin
177 N := -1;
178 F := False;
179 C := 0;
180 for I := Low(NetClients) to High(NetClients) do
181 begin
182 if NetClients[I].Used then
183 Inc(C)
184 else
185 if not F then
186 begin
187 F := True;
188 N := I;
189 end;
190 end;
191 if C >= NetMaxClients then
192 begin
193 Result := -1;
194 Exit;
195 end;
197 if not F then
198 begin
199 if (Length(NetClients) >= NetMaxClients) then
200 N := -1
201 else
202 begin
203 SetLength(NetClients, Length(NetClients) + 1);
204 N := High(NetClients);
205 end;
206 end;
208 if N >= 0 then
209 begin
210 NetClients[N].Used := True;
211 NetClients[N].ID := N;
212 NetClients[N].RequestedFullUpdate := False;
213 NetClients[N].RCONAuth := False;
214 NetClients[N].Voted := False;
215 NetClients[N].Player := 0;
216 end;
218 Result := N;
219 end;
221 function g_Net_Init(): Boolean;
222 var
223 F: TextFile;
224 IPstr: string;
225 IP: LongWord;
226 begin
227 e_Buffer_Clear(@NetIn);
228 e_Buffer_Clear(@NetOut);
229 SetLength(NetClients, 0);
230 NetPeer := nil;
231 NetHost := nil;
232 NetMyID := -1;
233 NetPlrUID1 := -1;
234 NetPlrUID2 := -1;
235 NetAddr.port := 25666;
236 SetLength(NetBannedHosts, 0);
237 if FileExists(DataDir + BANLIST_FILENAME) then
238 begin
239 Assign(F, DataDir + BANLIST_FILENAME);
240 Reset(F);
241 while not EOF(F) do
242 begin
243 Readln(F, IPstr);
244 if StrToIp(IPstr, IP) then
245 g_Net_BanHost(IP);
246 end;
247 CloseFile(F);
248 g_Net_SaveBanList();
249 end;
251 Result := (enet_initialize() = 0);
252 end;
254 procedure g_Net_Flush();
255 begin
256 enet_host_flush(NetHost);
257 end;
259 procedure g_Net_Cleanup();
260 begin
261 e_Buffer_Clear(@NetIn);
262 e_Buffer_Clear(@NetOut);
264 SetLength(NetClients, 0);
265 NetClientCount := 0;
267 NetPeer := nil;
268 NetHost := nil;
269 NetMPeer := nil;
270 NetMHost := nil;
271 NetMyID := -1;
272 NetPlrUID1 := -1;
273 NetPlrUID2 := -1;
274 NetState := NET_STATE_NONE;
276 NetPongSock := ENET_SOCKET_NULL;
278 NetTimeToMaster := 0;
279 NetTimeToUpdate := 0;
280 NetTimeToReliable := 0;
282 NetMode := NET_NONE;
283 end;
285 procedure g_Net_Free();
286 begin
287 g_Net_Cleanup();
289 enet_deinitialize();
290 NetInitDone := False;
291 end;
294 { /// SERVER FUNCTIONS /// }
297 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
298 begin
299 if NetMode <> NET_NONE then
300 begin
301 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
302 Result := False;
303 Exit;
304 end;
306 Result := True;
308 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
309 if not NetInitDone then
310 begin
311 if (not g_Net_Init()) then
312 begin
313 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
314 Result := False;
315 Exit;
316 end
317 else
318 NetInitDone := True;
319 end;
321 NetAddr.host := IPAddr;
322 NetAddr.port := Port;
324 NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
326 if (NetHost = nil) then
327 begin
328 g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
329 Result := False;
330 g_Net_Cleanup;
331 Exit;
332 end;
334 NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
335 if NetPongSock <> ENET_SOCKET_NULL then
336 begin
337 NetPongAddr.host := IPAddr;
338 NetPongAddr.port := Port + 1;
339 if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
340 begin
341 enet_socket_destroy(NetPongSock);
342 NetPongSock := ENET_SOCKET_NULL;
343 end
344 else
345 enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
346 end;
348 NetMode := NET_SERVER;
349 e_Buffer_Clear(@NetOut);
350 end;
352 procedure g_Net_Host_Die();
353 var
354 I: Integer;
355 begin
356 if NetMode <> NET_SERVER then Exit;
358 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
359 for I := 0 to High(NetClients) do
360 if NetClients[I].Used then
361 enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
363 while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
364 if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
365 enet_packet_destroy(NetEvent.packet);
367 for I := 0 to High(NetClients) do
368 if NetClients[I].Used then
369 begin
370 FreeMemory(NetClients[I].Peer^.data);
371 NetClients[I].Peer^.data := nil;
372 enet_peer_reset(NetClients[I].Peer);
373 NetClients[I].Peer := nil;
374 NetClients[I].Used := False;
375 end;
377 if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
378 if NetPongSock <> ENET_SOCKET_NULL then
379 enet_socket_destroy(NetPongSock);
381 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
382 enet_host_destroy(NetHost);
384 NetMode := NET_NONE;
386 g_Net_Cleanup;
387 e_WriteLog('NET: Server stopped', MSG_NOTIFY);
388 end;
391 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
392 var
393 P: pENetPacket;
394 F: enet_uint32;
395 begin
396 if (Reliable) then
397 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
398 else
399 F := 0;
401 if (ID >= 0) then
402 begin
403 if ID > High(NetClients) then Exit;
404 if NetClients[ID].Peer = nil then Exit;
406 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
407 if not Assigned(P) then Exit;
409 enet_peer_send(NetClients[ID].Peer, Chan, P);
410 end
411 else
412 begin
413 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
414 if not Assigned(P) then Exit;
416 enet_host_widecast(NetHost, Chan, P);
417 end;
419 g_Net_Flush();
420 e_Buffer_Clear(@NetOut);
421 end;
423 procedure g_Net_Host_CheckPings();
424 var
425 ClAddr: ENetAddress;
426 Buf: ENetBuffer;
427 Len, ClTime: Integer;
428 Ping: array [0..5] of Byte;
429 NPl: Byte;
430 begin
431 if NetPongSock = ENET_SOCKET_NULL then Exit;
433 Buf.data := Addr(Ping[0]);
434 Buf.dataLength := 6;
436 Ping[0] := 0;
438 Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
439 if Len < 0 then Exit;
441 if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
442 begin
443 ClTime := Integer(Addr(Ping[2])^);
445 e_Buffer_Clear(@NetOut);
446 e_Buffer_Write(@NetOut, Byte(Ord('D')));
447 e_Buffer_Write(@NetOut, Byte(Ord('F')));
448 e_Buffer_Write(@NetOut, ClTime);
449 g_Net_Slist_WriteInfo();
450 NPl := 0;
451 if gPlayer1 <> nil then Inc(NPl);
452 if gPlayer2 <> nil then Inc(NPl);
453 e_Buffer_Write(@NetOut, NPl);
454 e_Buffer_Write(@NetOut, gNumBots);
456 Buf.data := Addr(NetOut.Data[0]);
457 Buf.dataLength := NetOut.WritePos;
458 enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
460 e_Buffer_Clear(@NetOut);
461 end;
462 end;
464 function g_Net_Host_Update(): enet_size_t;
465 var
466 IP: string;
467 Port: Word;
468 ID: Integer;
469 TC: pTNetClient;
470 TP: TPlayer;
471 begin
472 IP := '';
473 Result := 0;
475 if NetUseMaster then
476 begin
477 g_Net_Slist_Check;
478 g_Net_Host_CheckPings;
479 end;
481 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
482 begin
483 case (NetEvent.kind) of
484 ENET_EVENT_TYPE_CONNECT:
485 begin
486 IP := IpToStr(NetEvent.Peer^.address.host);
487 Port := NetEvent.Peer^.address.port;
488 g_Console_Add(_lc[I_NET_MSG] +
489 Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
491 if (NetEvent.data <> NET_PROTOCOL_VER) then
492 begin
493 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
494 _lc[I_NET_DISC_PROTOCOL]);
495 NetEvent.peer^.data := GetMemory(SizeOf(Byte));
496 Byte(NetEvent.peer^.data^) := 255;
497 enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
498 enet_host_flush(NetHost);
499 Exit;
500 end;
502 ID := g_Net_FindSlot();
504 if ID < 0 then
505 begin
506 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
507 _lc[I_NET_DISC_FULL]);
508 NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
509 Byte(NetEvent.peer^.data^) := 255;
510 enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
511 enet_host_flush(NetHost);
512 Exit;
513 end;
515 NetClients[ID].Peer := NetEvent.peer;
516 NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
517 Byte(NetClients[ID].Peer^.data^) := ID;
518 NetClients[ID].State := NET_STATE_AUTH;
519 NetClients[ID].RCONAuth := False;
521 enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
523 Inc(NetClientCount);
524 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
525 end;
527 ENET_EVENT_TYPE_RECEIVE:
528 begin
529 ID := Byte(NetEvent.peer^.data^);
530 if ID > High(NetClients) then Exit;
531 TC := @NetClients[ID];
533 g_Net_HostMsgHandler(TC, NetEvent.packet);
534 end;
536 ENET_EVENT_TYPE_DISCONNECT:
537 begin
538 ID := Byte(NetEvent.peer^.data^);
539 if ID > High(NetClients) then Exit;
540 TC := @NetClients[ID];
541 if TC = nil then Exit;
543 if not (TC^.Used) then Exit;
545 TP := g_Player_Get(TC^.Player);
547 if TP <> nil then
548 begin
549 TP.Lives := 0;
550 TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
551 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
552 e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', MSG_NOTIFY);
553 g_Player_Remove(TP.UID);
554 end;
556 TC^.Used := False;
557 TC^.State := NET_STATE_NONE;
558 TC^.Peer := nil;
559 TC^.Player := 0;
560 TC^.RequestedFullUpdate := False;
562 FreeMemory(NetEvent.peer^.data);
563 NetEvent.peer^.data := nil;
564 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
565 Dec(NetClientCount);
567 if NetUseMaster then g_Net_Slist_Update;
568 end;
569 end;
570 end;
571 end;
574 { /// CLIENT FUNCTIONS /// }
577 procedure g_Net_Disconnect(Forced: Boolean = False);
578 begin
579 if NetMode <> NET_CLIENT then Exit;
580 if (NetHost = nil) or (NetPeer = nil) then Exit;
582 if not Forced then
583 begin
584 enet_peer_disconnect(NetPeer, NET_DISC_NONE);
586 while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
587 begin
588 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
589 begin
590 NetPeer := nil;
591 break;
592 end;
594 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
595 enet_packet_destroy(NetEvent.packet);
596 end;
598 if NetPeer <> nil then
599 begin
600 enet_peer_reset(NetPeer);
601 NetPeer := nil;
602 end;
603 end
604 else
605 begin
606 e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), MSG_NOTIFY);
607 if (NetEvent.data <= NET_DISC_MAX) then
608 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
609 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
610 end;
612 if NetHost <> nil then
613 begin
614 enet_host_destroy(NetHost);
615 NetHost := nil;
616 end;
617 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
619 g_Net_Cleanup;
620 e_WriteLog('NET: Disconnected', MSG_NOTIFY);
621 end;
623 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
624 var
625 P: pENetPacket;
626 F: enet_uint32;
627 begin
628 if (Reliable) then
629 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
630 else
631 F := 0;
633 P := enet_packet_create(Addr(NetOut.Data), NetOut.Len, F);
634 if not Assigned(P) then Exit;
636 enet_peer_send(NetPeer, Chan, P);
637 g_Net_Flush();
638 e_Buffer_Clear(@NetOut);
639 end;
641 function g_Net_Client_Update(): enet_size_t;
642 begin
643 Result := 0;
644 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
645 begin
646 case NetEvent.kind of
647 ENET_EVENT_TYPE_RECEIVE:
648 g_Net_ClientMsgHandler(NetEvent.packet);
650 ENET_EVENT_TYPE_DISCONNECT:
651 begin
652 g_Net_Disconnect(True);
653 Result := 1;
654 Exit;
655 end;
656 end;
657 end
658 end;
660 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
661 begin
662 Result := 0;
663 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
664 begin
665 case NetEvent.kind of
666 ENET_EVENT_TYPE_RECEIVE:
667 g_Net_ClientLightMsgHandler(NetEvent.packet);
669 ENET_EVENT_TYPE_DISCONNECT:
670 begin
671 g_Net_Disconnect(True);
672 Result := 1;
673 Exit;
674 end;
675 end;
676 end;
677 g_Net_Flush();
678 end;
680 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
681 var
682 OuterLoop: Boolean;
683 begin
684 if NetMode <> NET_NONE then
685 begin
686 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
687 Result := False;
688 Exit;
689 end;
691 Result := True;
693 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
694 [IP, Port]));
695 if not NetInitDone then
696 begin
697 if (not g_Net_Init()) then
698 begin
699 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
700 Result := False;
701 Exit;
702 end
703 else
704 NetInitDone := True;
705 end;
707 NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
709 if (NetHost = nil) then
710 begin
711 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
712 g_Net_Cleanup;
713 Result := False;
714 Exit;
715 end;
717 enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
718 NetAddr.port := Port;
720 NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
722 if (NetPeer = nil) then
723 begin
724 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
725 enet_host_destroy(NetHost);
726 g_Net_Cleanup;
727 Result := False;
728 Exit;
729 end;
731 OuterLoop := True;
732 while OuterLoop do
733 begin
734 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
735 begin
736 if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
737 begin
738 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
739 NetMode := NET_CLIENT;
740 e_Buffer_Clear(@NetOut);
741 enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
742 NetClientIP := IP;
743 NetClientPort := Port;
744 Exit;
745 end;
746 end;
748 ProcessLoading();
750 e_PollInput();
752 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
753 OuterLoop := False;
754 end;
756 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
757 if NetPeer <> nil then enet_peer_reset(NetPeer);
758 if NetHost <> nil then
759 begin
760 enet_host_destroy(NetHost);
761 NetHost := nil;
762 end;
763 g_Net_Cleanup();
764 Result := False;
765 end;
767 function IpToStr(IP: LongWord): string;
768 var
769 Ptr: Pointer;
770 begin
771 Result := '';
772 Ptr := Addr(IP);
774 e_Raw_Seek(0);
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 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr));
779 e_Raw_Seek(0);
780 end;
782 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
783 var
784 EAddr: ENetAddress;
785 begin
786 Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
787 IP := EAddr.host;
788 end;
790 function g_Net_Client_ByName(Name: string): pTNetClient;
791 var
792 a: Integer;
793 pl: TPlayer;
794 begin
795 Result := nil;
796 for a := Low(NetClients) to High(NetClients) do
797 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
798 begin
799 pl := g_Player_Get(NetClients[a].Player);
800 if pl = nil then continue;
801 if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
802 if NetClients[a].Peer <> nil then
803 begin
804 Result := @NetClients[a];
805 Exit;
806 end;
807 end;
808 end;
810 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
811 var
812 a: Integer;
813 begin
814 Result := nil;
815 for a := Low(NetClients) to High(NetClients) do
816 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
817 if NetClients[a].Player = PID then
818 begin
819 Result := @NetClients[a];
820 Exit;
821 end;
822 end;
824 function g_Net_ClientName_ByID(ID: Integer): string;
825 var
826 a: Integer;
827 pl: TPlayer;
828 begin
829 Result := '';
830 if ID = NET_EVERYONE then
831 Exit;
832 for a := Low(NetClients) to High(NetClients) do
833 if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
834 begin
835 pl := g_Player_Get(NetClients[a].Player);
836 if pl = nil then Exit;
837 Result := pl.Name;
838 end;
839 end;
841 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
842 var
843 P: pENetPacket;
844 F: enet_uint32;
845 dataLength: Cardinal;
846 begin
847 dataLength := Length(Data);
849 if (Reliable) then
850 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
851 else
852 F := 0;
854 if (peer <> nil) then
855 begin
856 P := enet_packet_create(@Data[0], dataLength, F);
857 if not Assigned(P) then Exit;
858 enet_peer_send(peer, Chan, P);
859 end
860 else
861 begin
862 P := enet_packet_create(@Data[0], dataLength, F);
863 if not Assigned(P) then Exit;
864 enet_host_widecast(NetHost, Chan, P);
865 end;
867 enet_host_flush(NetHost);
868 end;
870 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
871 var
872 downloadEvent: ENetEvent;
873 OuterLoop: Boolean;
874 MID: Byte;
875 Ptr: Pointer;
876 msgStream: TMemoryStream;
877 begin
878 FillChar(downloadEvent, SizeOf(downloadEvent), 0);
879 msgStream := nil;
880 OuterLoop := True;
881 while OuterLoop do
882 begin
883 while (enet_host_service(NetHost, @downloadEvent, 0) > 0) do
884 begin
885 if (downloadEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
886 begin
887 Ptr := downloadEvent.packet^.data;
889 MID := Byte(Ptr^);
891 if (MID = msgId) then
892 begin
893 msgStream := TMemoryStream.Create;
894 msgStream.SetSize(downloadEvent.packet^.dataLength);
895 msgStream.WriteBuffer(Ptr^, downloadEvent.packet^.dataLength);
896 msgStream.Seek(0, soFromBeginning);
898 OuterLoop := False;
899 enet_packet_destroy(downloadEvent.packet);
900 break;
901 end
902 else begin
903 enet_packet_destroy(downloadEvent.packet);
904 end;
905 end
906 else
907 if (downloadEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
908 begin
909 if (downloadEvent.data <= NET_DISC_MAX) then
910 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
911 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + downloadEvent.data)], True);
912 OuterLoop := False;
913 Break;
914 end;
915 end;
917 ProcessLoading();
919 e_PollInput();
921 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
922 break;
923 end;
924 Result := msgStream;
925 end;
927 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
928 var
929 I: Integer;
930 begin
931 Result := False;
932 if NetBannedHosts = nil then
933 Exit;
934 for I := 0 to High(NetBannedHosts) do
935 if (NetBannedHosts[I].IP = IP) and ((not Perm) or (NetBannedHosts[I].Perm)) then
936 begin
937 Result := True;
938 break;
939 end;
940 end;
942 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
943 var
944 I, P: Integer;
945 begin
946 if IP = 0 then
947 Exit;
948 if g_Net_IsHostBanned(IP, Perm) then
949 Exit;
951 P := -1;
952 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
953 if NetBannedHosts[I].IP = 0 then
954 begin
955 P := I;
956 break;
957 end;
959 if P < 0 then
960 begin
961 SetLength(NetBannedHosts, Length(NetBannedHosts) + 1);
962 P := High(NetBannedHosts);
963 end;
965 NetBannedHosts[P].IP := IP;
966 NetBannedHosts[P].Perm := Perm;
967 end;
969 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
970 var
971 a: LongWord;
972 b: Boolean;
973 begin
974 b := StrToIp(IP, a);
975 if b then
976 g_Net_BanHost(a, Perm);
977 end;
979 procedure g_Net_UnbanNonPermHosts();
980 var
981 I: Integer;
982 begin
983 if NetBannedHosts = nil then
984 Exit;
985 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
986 if (NetBannedHosts[I].IP > 0) and not NetBannedHosts[I].Perm then
987 begin
988 NetBannedHosts[I].IP := 0;
989 NetBannedHosts[I].Perm := True;
990 end;
991 end;
993 function g_Net_UnbanHost(IP: string): Boolean; overload;
994 var
995 a: LongWord;
996 begin
997 Result := StrToIp(IP, a);
998 if Result then
999 Result := g_Net_UnbanHost(a);
1000 end;
1002 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
1003 var
1004 I: Integer;
1005 begin
1006 Result := False;
1007 if IP = 0 then
1008 Exit;
1009 if NetBannedHosts = nil then
1010 Exit;
1011 for I := 0 to High(NetBannedHosts) do
1012 if NetBannedHosts[I].IP = IP then
1013 begin
1014 NetBannedHosts[I].IP := 0;
1015 NetBannedHosts[I].Perm := True;
1016 Result := True;
1017 // no break here to clear all bans of this host, perm and non-perm
1018 end;
1019 end;
1021 procedure g_Net_SaveBanList();
1022 var
1023 F: TextFile;
1024 I: Integer;
1025 begin
1026 Assign(F, DataDir + BANLIST_FILENAME);
1027 Rewrite(F);
1028 if NetBannedHosts <> nil then
1029 for I := 0 to High(NetBannedHosts) do
1030 if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
1031 Writeln(F, IpToStr(NetBannedHosts[I].IP));
1032 CloseFile(F);
1033 end;
1035 end.