DEADSOFTWARE

Merge branch 'master' of ssh://repo.or.cz/d2df-sdl
[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 ../shared/a_modes.inc}
17 unit g_net;
19 interface
21 uses
22 e_log, e_msg, ENet, Classes, MAPDEF;
24 const
25 NET_PROTOCOL_VER = 173;
27 NET_MAXCLIENTS = 24;
28 NET_CHANS = 11;
30 NET_CHAN_SERVICE = 0;
31 NET_CHAN_IMPORTANT = 1;
32 NET_CHAN_GAME = 2;
33 NET_CHAN_PLAYER = 3;
34 NET_CHAN_PLAYERPOS = 4;
35 NET_CHAN_MONSTER = 5;
36 NET_CHAN_MONSTERPOS = 6;
37 NET_CHAN_LARGEDATA = 7;
38 NET_CHAN_CHAT = 8;
39 NET_CHAN_DOWNLOAD = 9;
40 NET_CHAN_SHOTS = 10;
42 NET_NONE = 0;
43 NET_SERVER = 1;
44 NET_CLIENT = 2;
46 NET_BUFSIZE = $FFFF;
48 NET_EVERYONE = -1;
50 NET_DISC_NONE: enet_uint32 = 0;
51 NET_DISC_PROTOCOL: enet_uint32 = 1;
52 NET_DISC_VERSION: enet_uint32 = 2;
53 NET_DISC_FULL: enet_uint32 = 3;
54 NET_DISC_KICK: enet_uint32 = 4;
55 NET_DISC_DOWN: enet_uint32 = 5;
56 NET_DISC_PASSWORD: enet_uint32 = 6;
57 NET_DISC_TEMPBAN: enet_uint32 = 7;
58 NET_DISC_BAN: enet_uint32 = 8;
59 NET_DISC_MAX: enet_uint32 = 8;
61 NET_STATE_NONE = 0;
62 NET_STATE_AUTH = 1;
63 NET_STATE_GAME = 2;
65 BANLIST_FILENAME = 'banlist.txt';
66 NETDUMP_FILENAME = 'netdump';
68 type
69 TNetClient = record
70 ID: Byte;
71 Used: Boolean;
72 State: Byte;
73 Peer: pENetPeer;
74 Player: Word;
75 RequestedFullUpdate: Boolean;
76 RCONAuth: Boolean;
77 Voted: Boolean;
78 end;
79 TBanRecord = record
80 IP: LongWord;
81 Perm: Boolean;
82 end;
83 pTNetClient = ^TNetClient;
85 AByte = array of Byte;
87 var
88 NetInitDone: Boolean = False;
89 NetMode: Byte = NET_NONE;
90 NetDump: Boolean = False;
92 NetServerName: string = 'Unnamed Server';
93 NetPassword: string = '';
94 NetPort: Word = 25666;
96 NetAllowRCON: Boolean = False;
97 NetRCONPassword: string = '';
99 NetTimeToUpdate: Cardinal = 0;
100 NetTimeToReliable: Cardinal = 0;
101 NetTimeToMaster: Cardinal = 0;
103 NetHost: pENetHost = nil;
104 NetPeer: pENetPeer = nil;
105 NetEvent: ENetEvent;
106 NetAddr: ENetAddress;
108 NetPongAddr: ENetAddress;
109 NetPongSock: ENetSocket = ENET_SOCKET_NULL;
111 NetUseMaster: Boolean = True;
112 NetSlistAddr: ENetAddress;
113 NetSlistIP: string = 'mpms.doom2d.org';
114 NetSlistPort: Word = 25665;
116 NetClientIP: string = '127.0.0.1';
117 NetClientPort: Word = 25666;
119 NetIn, NetOut: TMsg;
121 NetClients: array of TNetClient;
122 NetClientCount: Byte = 0;
123 NetMaxClients: Byte = 255;
124 NetBannedHosts: array of TBanRecord;
126 NetState: Integer = NET_STATE_NONE;
128 NetMyID: Integer = -1;
129 NetPlrUID1: Integer = -1;
130 NetPlrUID2: Integer = -1;
132 NetInterpLevel: Integer = 1;
133 NetUpdateRate: Cardinal = 0; // as soon as possible
134 NetRelupdRate: Cardinal = 18; // around two times a second
135 NetMasterRate: Cardinal = 60000;
137 NetForcePlayerUpdate: Boolean = False;
138 NetPredictSelf: Boolean = True;
139 NetGotKeys: Boolean = False;
141 NetGotEverything: Boolean = False;
143 NetDumpFile: TStream;
145 function g_Net_Init(): Boolean;
146 procedure g_Net_Cleanup();
147 procedure g_Net_Free();
148 procedure g_Net_Flush();
150 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
151 procedure g_Net_Host_Die();
152 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
153 function g_Net_Host_Update(): enet_size_t;
155 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
156 procedure g_Net_Disconnect(Forced: Boolean = False);
157 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
158 function g_Net_Client_Update(): enet_size_t;
159 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
161 function g_Net_Client_ByName(Name: string): pTNetClient;
162 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
163 function g_Net_ClientName_ByID(ID: Integer): string;
165 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
166 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
168 function IpToStr(IP: LongWord): string;
169 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
171 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
172 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
173 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
174 function g_Net_UnbanHost(IP: string): Boolean; overload;
175 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
176 procedure g_Net_UnbanNonPermHosts();
177 procedure g_Net_SaveBanList();
179 procedure g_Net_DumpStart();
180 procedure g_Net_DumpSendBuffer();
181 procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord);
182 procedure g_Net_DumpEnd();
184 implementation
186 uses
187 SysUtils,
188 e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
189 g_main, g_game, g_language, g_weapons, utils;
192 { /// SERVICE FUNCTIONS /// }
195 function g_Net_FindSlot(): Integer;
196 var
197 I: Integer;
198 F: Boolean;
199 N, C: Integer;
200 begin
201 N := -1;
202 F := False;
203 C := 0;
204 for I := Low(NetClients) to High(NetClients) do
205 begin
206 if NetClients[I].Used then
207 Inc(C)
208 else
209 if not F then
210 begin
211 F := True;
212 N := I;
213 end;
214 end;
215 if C >= NetMaxClients then
216 begin
217 Result := -1;
218 Exit;
219 end;
221 if not F then
222 begin
223 if (Length(NetClients) >= NetMaxClients) then
224 N := -1
225 else
226 begin
227 SetLength(NetClients, Length(NetClients) + 1);
228 N := High(NetClients);
229 end;
230 end;
232 if N >= 0 then
233 begin
234 NetClients[N].Used := True;
235 NetClients[N].ID := N;
236 NetClients[N].RequestedFullUpdate := False;
237 NetClients[N].RCONAuth := False;
238 NetClients[N].Voted := False;
239 NetClients[N].Player := 0;
240 end;
242 Result := N;
243 end;
245 function g_Net_Init(): Boolean;
246 var
247 F: TextFile;
248 IPstr: string;
249 IP: LongWord;
250 begin
251 NetIn.Clear();
252 NetOut.Clear();
253 SetLength(NetClients, 0);
254 NetPeer := nil;
255 NetHost := nil;
256 NetMyID := -1;
257 NetPlrUID1 := -1;
258 NetPlrUID2 := -1;
259 NetAddr.port := 25666;
260 SetLength(NetBannedHosts, 0);
261 if FileExists(DataDir + BANLIST_FILENAME) then
262 begin
263 Assign(F, DataDir + BANLIST_FILENAME);
264 Reset(F);
265 while not EOF(F) do
266 begin
267 Readln(F, IPstr);
268 if StrToIp(IPstr, IP) then
269 g_Net_BanHost(IP);
270 end;
271 CloseFile(F);
272 g_Net_SaveBanList();
273 end;
275 Result := (enet_initialize() = 0);
276 end;
278 procedure g_Net_Flush();
279 begin
280 enet_host_flush(NetHost);
281 end;
283 procedure g_Net_Cleanup();
284 begin
285 NetIn.Clear();
286 NetOut.Clear();
288 SetLength(NetClients, 0);
289 NetClientCount := 0;
291 NetPeer := nil;
292 NetHost := nil;
293 NetMPeer := nil;
294 NetMHost := nil;
295 NetMyID := -1;
296 NetPlrUID1 := -1;
297 NetPlrUID2 := -1;
298 NetState := NET_STATE_NONE;
300 NetPongSock := ENET_SOCKET_NULL;
302 NetTimeToMaster := 0;
303 NetTimeToUpdate := 0;
304 NetTimeToReliable := 0;
306 NetMode := NET_NONE;
308 if NetDump then
309 g_Net_DumpEnd();
310 end;
312 procedure g_Net_Free();
313 begin
314 g_Net_Cleanup();
316 enet_deinitialize();
317 NetInitDone := False;
318 end;
321 { /// SERVER FUNCTIONS /// }
324 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
325 begin
326 if NetMode <> NET_NONE then
327 begin
328 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
329 Result := False;
330 Exit;
331 end;
333 Result := True;
335 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
336 if not NetInitDone then
337 begin
338 if (not g_Net_Init()) then
339 begin
340 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
341 Result := False;
342 Exit;
343 end
344 else
345 NetInitDone := True;
346 end;
348 NetAddr.host := IPAddr;
349 NetAddr.port := Port;
351 NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
353 if (NetHost = nil) then
354 begin
355 g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
356 Result := False;
357 g_Net_Cleanup;
358 Exit;
359 end;
361 NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
362 if NetPongSock <> ENET_SOCKET_NULL then
363 begin
364 NetPongAddr.host := IPAddr;
365 NetPongAddr.port := Port + 1;
366 if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
367 begin
368 enet_socket_destroy(NetPongSock);
369 NetPongSock := ENET_SOCKET_NULL;
370 end
371 else
372 enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
373 end;
375 NetMode := NET_SERVER;
376 NetOut.Clear();
378 if NetDump then
379 g_Net_DumpStart();
380 end;
382 procedure g_Net_Host_Die();
383 var
384 I: Integer;
385 begin
386 if NetMode <> NET_SERVER then Exit;
388 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
389 for I := 0 to High(NetClients) do
390 if NetClients[I].Used then
391 enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
393 while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
394 if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
395 enet_packet_destroy(NetEvent.packet);
397 for I := 0 to High(NetClients) do
398 if NetClients[I].Used then
399 begin
400 FreeMemory(NetClients[I].Peer^.data);
401 NetClients[I].Peer^.data := nil;
402 enet_peer_reset(NetClients[I].Peer);
403 NetClients[I].Peer := nil;
404 NetClients[I].Used := False;
405 end;
407 if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
408 if NetPongSock <> ENET_SOCKET_NULL then
409 enet_socket_destroy(NetPongSock);
411 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
412 enet_host_destroy(NetHost);
414 NetMode := NET_NONE;
416 g_Net_Cleanup;
417 e_WriteLog('NET: Server stopped', TMsgType.Notify);
418 end;
421 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
422 var
423 P: pENetPacket;
424 F: enet_uint32;
425 begin
426 if (Reliable) then
427 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
428 else
429 F := 0;
431 if (ID >= 0) then
432 begin
433 if ID > High(NetClients) then Exit;
434 if NetClients[ID].Peer = nil then Exit;
436 P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
437 if not Assigned(P) then Exit;
439 enet_peer_send(NetClients[ID].Peer, Chan, P);
440 end
441 else
442 begin
443 P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
444 if not Assigned(P) then Exit;
446 enet_host_broadcast(NetHost, Chan, P);
447 end;
449 if NetDump then g_Net_DumpSendBuffer();
450 g_Net_Flush();
451 NetOut.Clear();
452 end;
454 procedure g_Net_Host_CheckPings();
455 var
456 ClAddr: ENetAddress;
457 Buf: ENetBuffer;
458 Len: Integer;
459 ClTime: Int64;
460 Ping: array [0..9] of Byte;
461 NPl: Byte;
462 begin
463 if NetPongSock = ENET_SOCKET_NULL then Exit;
465 Buf.data := Addr(Ping[0]);
466 Buf.dataLength := 2+8;
468 Ping[0] := 0;
470 Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
471 if Len < 0 then Exit;
473 if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
474 begin
475 ClTime := Int64(Addr(Ping[2])^);
477 NetOut.Clear();
478 NetOut.Write(Byte(Ord('D')));
479 NetOut.Write(Byte(Ord('F')));
480 NetOut.Write(ClTime);
481 g_Net_Slist_WriteInfo();
482 NPl := 0;
483 if gPlayer1 <> nil then Inc(NPl);
484 if gPlayer2 <> nil then Inc(NPl);
485 NetOut.Write(NPl);
486 NetOut.Write(gNumBots);
488 Buf.data := NetOut.Data;
489 Buf.dataLength := NetOut.CurSize;
490 enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
492 NetOut.Clear();
493 end;
494 end;
496 function g_Net_Host_Update(): enet_size_t;
497 var
498 IP: string;
499 Port: Word;
500 ID: Integer;
501 TC: pTNetClient;
502 TP: TPlayer;
503 begin
504 IP := '';
505 Result := 0;
507 if NetUseMaster then
508 begin
509 g_Net_Slist_Check;
510 g_Net_Host_CheckPings;
511 end;
513 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
514 begin
515 case (NetEvent.kind) of
516 ENET_EVENT_TYPE_CONNECT:
517 begin
518 IP := IpToStr(NetEvent.Peer^.address.host);
519 Port := NetEvent.Peer^.address.port;
520 g_Console_Add(_lc[I_NET_MSG] +
521 Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
523 if (NetEvent.data <> NET_PROTOCOL_VER) then
524 begin
525 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
526 _lc[I_NET_DISC_PROTOCOL]);
527 NetEvent.peer^.data := GetMemory(SizeOf(Byte));
528 Byte(NetEvent.peer^.data^) := 255;
529 enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
530 enet_host_flush(NetHost);
531 Exit;
532 end;
534 ID := g_Net_FindSlot();
536 if ID < 0 then
537 begin
538 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
539 _lc[I_NET_DISC_FULL]);
540 NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
541 Byte(NetEvent.peer^.data^) := 255;
542 enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
543 enet_host_flush(NetHost);
544 Exit;
545 end;
547 NetClients[ID].Peer := NetEvent.peer;
548 NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
549 Byte(NetClients[ID].Peer^.data^) := ID;
550 NetClients[ID].State := NET_STATE_AUTH;
551 NetClients[ID].RCONAuth := False;
553 enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
555 Inc(NetClientCount);
556 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
557 end;
559 ENET_EVENT_TYPE_RECEIVE:
560 begin
561 ID := Byte(NetEvent.peer^.data^);
562 if ID > High(NetClients) then Exit;
563 TC := @NetClients[ID];
565 if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
566 g_Net_HostMsgHandler(TC, NetEvent.packet);
567 end;
569 ENET_EVENT_TYPE_DISCONNECT:
570 begin
571 ID := Byte(NetEvent.peer^.data^);
572 if ID > High(NetClients) then Exit;
573 TC := @NetClients[ID];
574 if TC = nil then Exit;
576 if not (TC^.Used) then Exit;
578 TP := g_Player_Get(TC^.Player);
580 if TP <> nil then
581 begin
582 TP.Lives := 0;
583 TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
584 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
585 e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', TMsgType.Notify);
586 g_Player_Remove(TP.UID);
587 end;
589 TC^.Used := False;
590 TC^.State := NET_STATE_NONE;
591 TC^.Peer := nil;
592 TC^.Player := 0;
593 TC^.RequestedFullUpdate := False;
595 FreeMemory(NetEvent.peer^.data);
596 NetEvent.peer^.data := nil;
597 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
598 Dec(NetClientCount);
600 if NetUseMaster then g_Net_Slist_Update;
601 end;
602 end;
603 end;
604 end;
607 { /// CLIENT FUNCTIONS /// }
610 procedure g_Net_Disconnect(Forced: Boolean = False);
611 begin
612 if NetMode <> NET_CLIENT then Exit;
613 if (NetHost = nil) or (NetPeer = nil) then Exit;
615 if not Forced then
616 begin
617 enet_peer_disconnect(NetPeer, NET_DISC_NONE);
619 while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
620 begin
621 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
622 begin
623 NetPeer := nil;
624 break;
625 end;
627 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
628 enet_packet_destroy(NetEvent.packet);
629 end;
631 if NetPeer <> nil then
632 begin
633 enet_peer_reset(NetPeer);
634 NetPeer := nil;
635 end;
636 end
637 else
638 begin
639 e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), TMsgType.Notify);
640 if (NetEvent.data <= NET_DISC_MAX) then
641 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
642 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
643 end;
645 if NetHost <> nil then
646 begin
647 enet_host_destroy(NetHost);
648 NetHost := nil;
649 end;
650 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
652 g_Net_Cleanup;
653 e_WriteLog('NET: Disconnected', TMsgType.Notify);
654 end;
656 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
657 var
658 P: pENetPacket;
659 F: enet_uint32;
660 begin
661 if (Reliable) then
662 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
663 else
664 F := 0;
666 P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
667 if not Assigned(P) then Exit;
669 enet_peer_send(NetPeer, Chan, P);
670 if NetDump then g_Net_DumpSendBuffer();
671 g_Net_Flush();
672 NetOut.Clear();
673 end;
675 function g_Net_Client_Update(): enet_size_t;
676 begin
677 Result := 0;
678 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
679 begin
680 case NetEvent.kind of
681 ENET_EVENT_TYPE_RECEIVE:
682 begin
683 if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
684 g_Net_ClientMsgHandler(NetEvent.packet);
685 end;
687 ENET_EVENT_TYPE_DISCONNECT:
688 begin
689 g_Net_Disconnect(True);
690 Result := 1;
691 Exit;
692 end;
693 end;
694 end
695 end;
697 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
698 begin
699 Result := 0;
700 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
701 begin
702 case NetEvent.kind of
703 ENET_EVENT_TYPE_RECEIVE:
704 begin
705 if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
706 g_Net_ClientLightMsgHandler(NetEvent.packet);
707 end;
709 ENET_EVENT_TYPE_DISCONNECT:
710 begin
711 g_Net_Disconnect(True);
712 Result := 1;
713 Exit;
714 end;
715 end;
716 end;
717 g_Net_Flush();
718 end;
720 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
721 var
722 OuterLoop: Boolean;
723 begin
724 if NetMode <> NET_NONE then
725 begin
726 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
727 Result := False;
728 Exit;
729 end;
731 Result := True;
733 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
734 [IP, Port]));
735 if not NetInitDone then
736 begin
737 if (not g_Net_Init()) then
738 begin
739 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
740 Result := False;
741 Exit;
742 end
743 else
744 NetInitDone := True;
745 end;
747 NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
749 if (NetHost = nil) then
750 begin
751 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
752 g_Net_Cleanup;
753 Result := False;
754 Exit;
755 end;
757 enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
758 NetAddr.port := Port;
760 NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
762 if (NetPeer = nil) then
763 begin
764 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
765 enet_host_destroy(NetHost);
766 g_Net_Cleanup;
767 Result := False;
768 Exit;
769 end;
771 OuterLoop := True;
772 while OuterLoop do
773 begin
774 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
775 begin
776 if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
777 begin
778 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
779 NetMode := NET_CLIENT;
780 NetOut.Clear();
781 enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
782 NetClientIP := IP;
783 NetClientPort := Port;
784 if NetDump then
785 g_Net_DumpStart();
786 Exit;
787 end;
788 end;
790 ProcessLoading(true);
792 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
793 OuterLoop := False;
794 end;
796 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
797 if NetPeer <> nil then enet_peer_reset(NetPeer);
798 if NetHost <> nil then
799 begin
800 enet_host_destroy(NetHost);
801 NetHost := nil;
802 end;
803 g_Net_Cleanup();
804 Result := False;
805 end;
807 function IpToStr(IP: LongWord): string;
808 var
809 Ptr: Pointer;
810 begin
811 Ptr := Addr(IP);
812 Result := IntToStr(PByte(Ptr + 0)^) + '.';
813 Result := Result + IntToStr(PByte(Ptr + 1)^) + '.';
814 Result := Result + IntToStr(PByte(Ptr + 2)^) + '.';
815 Result := Result + IntToStr(PByte(Ptr + 3)^);
816 end;
818 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
819 var
820 EAddr: ENetAddress;
821 begin
822 Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
823 IP := EAddr.host;
824 end;
826 function g_Net_Client_ByName(Name: string): pTNetClient;
827 var
828 a: Integer;
829 pl: TPlayer;
830 begin
831 Result := nil;
832 for a := Low(NetClients) to High(NetClients) do
833 if (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 continue;
837 if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
838 if NetClients[a].Peer <> nil then
839 begin
840 Result := @NetClients[a];
841 Exit;
842 end;
843 end;
844 end;
846 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
847 var
848 a: Integer;
849 begin
850 Result := nil;
851 for a := Low(NetClients) to High(NetClients) do
852 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
853 if NetClients[a].Player = PID then
854 begin
855 Result := @NetClients[a];
856 Exit;
857 end;
858 end;
860 function g_Net_ClientName_ByID(ID: Integer): string;
861 var
862 a: Integer;
863 pl: TPlayer;
864 begin
865 Result := '';
866 if ID = NET_EVERYONE then
867 Exit;
868 for a := Low(NetClients) to High(NetClients) do
869 if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
870 begin
871 pl := g_Player_Get(NetClients[a].Player);
872 if pl = nil then Exit;
873 Result := pl.Name;
874 end;
875 end;
877 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
878 var
879 P: pENetPacket;
880 F: enet_uint32;
881 dataLength: Cardinal;
882 begin
883 dataLength := Length(Data);
885 if (Reliable) then
886 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
887 else
888 F := 0;
890 if (peer <> nil) then
891 begin
892 P := enet_packet_create(@Data[0], dataLength, F);
893 if not Assigned(P) then Exit;
894 enet_peer_send(peer, Chan, P);
895 end
896 else
897 begin
898 P := enet_packet_create(@Data[0], dataLength, F);
899 if not Assigned(P) then Exit;
900 enet_host_broadcast(NetHost, Chan, P);
901 end;
903 enet_host_flush(NetHost);
904 end;
906 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
907 var
908 downloadEvent: ENetEvent;
909 OuterLoop: Boolean;
910 MID: Byte;
911 Ptr: Pointer;
912 msgStream: TMemoryStream;
913 begin
914 FillChar(downloadEvent, SizeOf(downloadEvent), 0);
915 msgStream := nil;
916 OuterLoop := True;
917 while OuterLoop do
918 begin
919 while (enet_host_service(NetHost, @downloadEvent, 0) > 0) do
920 begin
921 if (downloadEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
922 begin
923 Ptr := downloadEvent.packet^.data;
925 MID := Byte(Ptr^);
927 if (MID = msgId) then
928 begin
929 msgStream := TMemoryStream.Create;
930 msgStream.SetSize(downloadEvent.packet^.dataLength);
931 msgStream.WriteBuffer(Ptr^, downloadEvent.packet^.dataLength);
932 msgStream.Seek(0, soFromBeginning);
934 OuterLoop := False;
935 enet_packet_destroy(downloadEvent.packet);
936 break;
937 end
938 else begin
939 enet_packet_destroy(downloadEvent.packet);
940 end;
941 end
942 else
943 if (downloadEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
944 begin
945 if (downloadEvent.data <= NET_DISC_MAX) then
946 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
947 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + downloadEvent.data)], True);
948 OuterLoop := False;
949 Break;
950 end;
951 end;
953 ProcessLoading(true);
955 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
956 break;
957 end;
958 Result := msgStream;
959 end;
961 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
962 var
963 I: Integer;
964 begin
965 Result := False;
966 if NetBannedHosts = nil then
967 Exit;
968 for I := 0 to High(NetBannedHosts) do
969 if (NetBannedHosts[I].IP = IP) and ((not Perm) or (NetBannedHosts[I].Perm)) then
970 begin
971 Result := True;
972 break;
973 end;
974 end;
976 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
977 var
978 I, P: Integer;
979 begin
980 if IP = 0 then
981 Exit;
982 if g_Net_IsHostBanned(IP, Perm) then
983 Exit;
985 P := -1;
986 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
987 if NetBannedHosts[I].IP = 0 then
988 begin
989 P := I;
990 break;
991 end;
993 if P < 0 then
994 begin
995 SetLength(NetBannedHosts, Length(NetBannedHosts) + 1);
996 P := High(NetBannedHosts);
997 end;
999 NetBannedHosts[P].IP := IP;
1000 NetBannedHosts[P].Perm := Perm;
1001 end;
1003 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
1004 var
1005 a: LongWord;
1006 b: Boolean;
1007 begin
1008 b := StrToIp(IP, a);
1009 if b then
1010 g_Net_BanHost(a, Perm);
1011 end;
1013 procedure g_Net_UnbanNonPermHosts();
1014 var
1015 I: Integer;
1016 begin
1017 if NetBannedHosts = nil then
1018 Exit;
1019 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
1020 if (NetBannedHosts[I].IP > 0) and not NetBannedHosts[I].Perm then
1021 begin
1022 NetBannedHosts[I].IP := 0;
1023 NetBannedHosts[I].Perm := True;
1024 end;
1025 end;
1027 function g_Net_UnbanHost(IP: string): Boolean; overload;
1028 var
1029 a: LongWord;
1030 begin
1031 Result := StrToIp(IP, a);
1032 if Result then
1033 Result := g_Net_UnbanHost(a);
1034 end;
1036 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
1037 var
1038 I: Integer;
1039 begin
1040 Result := False;
1041 if IP = 0 then
1042 Exit;
1043 if NetBannedHosts = nil then
1044 Exit;
1045 for I := 0 to High(NetBannedHosts) do
1046 if NetBannedHosts[I].IP = IP then
1047 begin
1048 NetBannedHosts[I].IP := 0;
1049 NetBannedHosts[I].Perm := True;
1050 Result := True;
1051 // no break here to clear all bans of this host, perm and non-perm
1052 end;
1053 end;
1055 procedure g_Net_SaveBanList();
1056 var
1057 F: TextFile;
1058 I: Integer;
1059 begin
1060 Assign(F, DataDir + BANLIST_FILENAME);
1061 Rewrite(F);
1062 if NetBannedHosts <> nil then
1063 for I := 0 to High(NetBannedHosts) do
1064 if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
1065 Writeln(F, IpToStr(NetBannedHosts[I].IP));
1066 CloseFile(F);
1067 end;
1069 procedure g_Net_DumpStart();
1070 begin
1071 if NetMode = NET_SERVER then
1072 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_server')
1073 else
1074 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_client');
1075 end;
1077 procedure g_Net_DumpSendBuffer();
1078 begin
1079 writeInt(NetDumpFile, gTime);
1080 writeInt(NetDumpFile, LongWord(NetOut.CurSize));
1081 writeInt(NetDumpFile, Byte(1));
1082 NetDumpFile.WriteBuffer(NetOut.Data^, NetOut.CurSize);
1083 end;
1085 procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord);
1086 begin
1087 if (Buf = nil) or (Len = 0) then Exit;
1088 writeInt(NetDumpFile, gTime);
1089 writeInt(NetDumpFile, Len);
1090 writeInt(NetDumpFile, Byte(0));
1091 NetDumpFile.WriteBuffer(Buf^, Len);
1092 end;
1094 procedure g_Net_DumpEnd();
1095 begin
1096 NetDumpFile.Free();
1097 NetDumpFile := nil;
1098 end;
1100 initialization
1102 NetIn.Alloc(NET_BUFSIZE);
1103 NetOut.Alloc(NET_BUFSIZE);
1105 finalization
1107 NetIn.Free();
1108 NetOut.Free();
1110 end.