DEADSOFTWARE

raised protocol version
[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();
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 e_Buffer_Clear(B);
223 end;
224 end;
226 function g_Net_FindSlot(): Integer;
227 var
228 I: Integer;
229 F: Boolean;
230 N, C: Integer;
231 begin
232 N := -1;
233 F := False;
234 C := 0;
235 for I := Low(NetClients) to High(NetClients) do
236 begin
237 if NetClients[I].Used then
238 Inc(C)
239 else
240 if not F then
241 begin
242 F := True;
243 N := I;
244 end;
245 end;
246 if C >= NetMaxClients then
247 begin
248 Result := -1;
249 Exit;
250 end;
252 if not F then
253 begin
254 if (Length(NetClients) >= NetMaxClients) then
255 N := -1
256 else
257 begin
258 SetLength(NetClients, Length(NetClients) + 1);
259 N := High(NetClients);
260 end;
261 end;
263 if N >= 0 then
264 begin
265 NetClients[N].Used := True;
266 NetClients[N].ID := N;
267 NetClients[N].RequestedFullUpdate := False;
268 NetClients[N].RCONAuth := False;
269 NetClients[N].Voted := False;
270 NetClients[N].Player := 0;
271 NetClients[N].Peer := nil;
272 for I := 0 to CH_MAX do
273 e_Buffer_Clear(Addr(NetClients[N].SendBuf[CH_MAX]));
274 end;
276 Result := N;
277 end;
279 function g_Net_Init(): Boolean;
280 var
281 F: TextFile;
282 IPstr: string;
283 IP: LongWord;
284 I: Integer;
285 begin
286 e_Buffer_Clear(@NetIn);
287 e_Buffer_Clear(@NetOut);
288 for I := 0 to CH_MAX do
289 e_Buffer_Clear(@NetSend[i]);
290 SetLength(NetClients, 0);
291 NetPeer := nil;
292 NetHost := nil;
293 NetMyID := -1;
294 NetPlrUID1 := -1;
295 NetPlrUID2 := -1;
296 NetAddr.port := 25666;
297 SetLength(NetBannedHosts, 0);
298 if FileExists(DataDir + BANLIST_FILENAME) then
299 begin
300 Assign(F, DataDir + BANLIST_FILENAME);
301 Reset(F);
302 while not EOF(F) do
303 begin
304 Readln(F, IPstr);
305 if StrToIp(IPstr, IP) then
306 g_Net_BanHost(IP);
307 end;
308 CloseFile(F);
309 g_Net_SaveBanList();
310 end;
312 Result := (enet_initialize() = 0);
313 end;
315 procedure g_Net_Flush();
316 begin
317 if NetMode = NET_SERVER then
318 g_Net_Host_FlushBuffers()
319 else
320 g_Net_Client_FlushBuffers();
321 enet_host_flush(NetHost);
322 end;
324 procedure g_Net_Cleanup();
325 var
326 I: Integer;
327 begin
328 e_Buffer_Clear(@NetIn);
329 e_Buffer_Clear(@NetOut);
330 for i := 0 to CH_MAX do
331 e_Buffer_Clear(@NetSend[i]);
333 SetLength(NetClients, 0);
334 NetClientCount := 0;
336 NetPeer := nil;
337 NetHost := nil;
338 NetMPeer := nil;
339 NetMHost := nil;
340 NetMyID := -1;
341 NetPlrUID1 := -1;
342 NetPlrUID2 := -1;
343 NetState := NET_STATE_NONE;
345 NetPongSock := ENET_SOCKET_NULL;
347 NetTimeToMaster := 0;
348 NetTimeToUpdate := 0;
349 NetTimeToReliable := 0;
351 NetMode := NET_NONE;
353 if NetDump then
354 g_Net_DumpEnd();
355 end;
357 procedure g_Net_Free();
358 begin
359 g_Net_Cleanup();
361 enet_deinitialize();
362 NetInitDone := False;
363 end;
366 { /// SERVER FUNCTIONS /// }
369 function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
370 begin
371 if NetMode <> NET_NONE then
372 begin
373 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
374 Result := False;
375 Exit;
376 end;
378 Result := True;
380 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
381 if not NetInitDone then
382 begin
383 if (not g_Net_Init()) then
384 begin
385 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
386 Result := False;
387 Exit;
388 end
389 else
390 NetInitDone := True;
391 end;
393 NetAddr.host := IPAddr;
394 NetAddr.port := Port;
396 NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
398 if (NetHost = nil) then
399 begin
400 g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
401 Result := False;
402 g_Net_Cleanup;
403 Exit;
404 end;
406 NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
407 if NetPongSock <> ENET_SOCKET_NULL then
408 begin
409 NetPongAddr.host := IPAddr;
410 NetPongAddr.port := Port + 1;
411 if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
412 begin
413 enet_socket_destroy(NetPongSock);
414 NetPongSock := ENET_SOCKET_NULL;
415 end
416 else
417 enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
418 end;
420 NetMode := NET_SERVER;
421 e_Buffer_Clear(@NetOut);
423 if NetDump then
424 g_Net_DumpStart();
425 end;
427 procedure g_Net_Host_Die();
428 var
429 I: Integer;
430 begin
431 if NetMode <> NET_SERVER then Exit;
433 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
434 for I := 0 to High(NetClients) do
435 if NetClients[I].Used then
436 enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
438 while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
439 if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
440 enet_packet_destroy(NetEvent.packet);
442 for I := 0 to High(NetClients) do
443 if NetClients[I].Used then
444 begin
445 FreeMemory(NetClients[I].Peer^.data);
446 NetClients[I].Peer^.data := nil;
447 enet_peer_reset(NetClients[I].Peer);
448 NetClients[I].Peer := nil;
449 NetClients[I].Used := False;
450 end;
452 if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
453 if NetPongSock <> ENET_SOCKET_NULL then
454 enet_socket_destroy(NetPongSock);
456 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
457 enet_host_destroy(NetHost);
459 NetMode := NET_NONE;
461 g_Net_Cleanup;
462 e_WriteLog('NET: Server stopped', MSG_NOTIFY);
463 end;
465 procedure g_Net_Host_FlushBuffers();
466 var
467 I: Integer;
468 begin
469 // send broadcast
470 SendBuffer(@NetSend[CH_RELIABLE], CH_RELIABLE, nil);
471 SendBuffer(@NetSend[CH_UNRELIABLE], CH_UNRELIABLE, nil);
472 // send to individual clients
473 if NetClients <> nil then
474 for I := Low(NetClients) to High(NetClients) do
475 with NetClients[I] do
476 begin
477 SendBuffer(@SendBuf[CH_RELIABLE], CH_RELIABLE, Peer);
478 SendBuffer(@SendBuf[CH_UNRELIABLE], CH_UNRELIABLE, Peer);
479 end;
480 end;
482 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
483 var
484 I: Integer;
485 B: pTBuffer;
486 begin
487 if (Reliable) then
488 I := CH_RELIABLE
489 else
490 I := CH_UNRELIABLE;
492 if (ID >= 0) then
493 begin
494 if (ID > High(NetClients)) or (NetClients[ID].Peer = nil) then
495 begin
496 e_Buffer_Clear(@NetOut);
497 Exit;
498 end;
499 B := Addr(NetClients[ID].SendBuf[I]);
500 end
501 else
502 begin
503 B := Addr(NetSend[I]);
504 end;
506 e_Buffer_Write(B, @NetOut);
507 e_Buffer_Clear(@NetOut);
508 end;
510 procedure g_Net_Host_CheckPings();
511 var
512 ClAddr: ENetAddress;
513 Buf: ENetBuffer;
514 Len: Integer;
515 ClTime: Int64;
516 Ping: array [0..9] of Byte;
517 NPl: Byte;
518 begin
519 if NetPongSock = ENET_SOCKET_NULL then Exit;
521 Buf.data := Addr(Ping[0]);
522 Buf.dataLength := 2+8;
524 Ping[0] := 0;
526 Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
527 if Len < 0 then Exit;
529 if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
530 begin
531 ClTime := Int64(Addr(Ping[2])^);
533 e_Buffer_Clear(@NetOut);
534 e_Buffer_Write(@NetOut, Byte(Ord('D')));
535 e_Buffer_Write(@NetOut, Byte(Ord('F')));
536 e_Buffer_Write(@NetOut, ClTime);
537 g_Net_Slist_WriteInfo();
538 NPl := 0;
539 if gPlayer1 <> nil then Inc(NPl);
540 if gPlayer2 <> nil then Inc(NPl);
541 e_Buffer_Write(@NetOut, NPl);
542 e_Buffer_Write(@NetOut, gNumBots);
544 Buf.data := Addr(NetOut.Data[0]);
545 Buf.dataLength := NetOut.WritePos;
546 enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
548 e_Buffer_Clear(@NetOut);
549 end;
550 end;
552 function g_Net_Host_Update(): enet_size_t;
553 var
554 IP: string;
555 Port: Word;
556 ID, I: Integer;
557 TC: pTNetClient;
558 TP: TPlayer;
559 begin
560 IP := '';
561 Result := 0;
563 if NetUseMaster then
564 begin
565 g_Net_Slist_Check;
566 g_Net_Host_CheckPings;
567 end;
569 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
570 begin
571 case (NetEvent.kind) of
572 ENET_EVENT_TYPE_CONNECT:
573 begin
574 IP := IpToStr(NetEvent.Peer^.address.host);
575 Port := NetEvent.Peer^.address.port;
576 g_Console_Add(_lc[I_NET_MSG] +
577 Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
579 if (NetEvent.data <> NET_PROTOCOL_VER) then
580 begin
581 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
582 _lc[I_NET_DISC_PROTOCOL]);
583 NetEvent.peer^.data := GetMemory(SizeOf(Byte));
584 Byte(NetEvent.peer^.data^) := 255;
585 enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
586 enet_host_flush(NetHost);
587 Exit;
588 end;
590 ID := g_Net_FindSlot();
592 if ID < 0 then
593 begin
594 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
595 _lc[I_NET_DISC_FULL]);
596 NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
597 Byte(NetEvent.peer^.data^) := 255;
598 enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
599 enet_host_flush(NetHost);
600 Exit;
601 end;
603 NetClients[ID].Peer := NetEvent.peer;
604 NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
605 Byte(NetClients[ID].Peer^.data^) := ID;
606 NetClients[ID].State := NET_STATE_AUTH;
607 NetClients[ID].RCONAuth := False;
609 enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
611 Inc(NetClientCount);
612 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
613 end;
615 ENET_EVENT_TYPE_RECEIVE:
616 begin
617 ID := Byte(NetEvent.peer^.data^);
618 if ID > High(NetClients) then Exit;
619 TC := @NetClients[ID];
620 g_Net_HostMsgHandler(TC, NetEvent.packet);
621 end;
623 ENET_EVENT_TYPE_DISCONNECT:
624 begin
625 ID := Byte(NetEvent.peer^.data^);
626 if ID > High(NetClients) then Exit;
627 TC := @NetClients[ID];
628 if TC = nil then Exit;
630 if not (TC^.Used) then Exit;
632 TP := g_Player_Get(TC^.Player);
634 if TP <> nil then
635 begin
636 TP.Lives := 0;
637 TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
638 g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
639 e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', MSG_NOTIFY);
640 g_Player_Remove(TP.UID);
641 end;
643 TC^.Used := False;
644 TC^.State := NET_STATE_NONE;
645 TC^.Peer := nil;
646 TC^.Player := 0;
647 TC^.RequestedFullUpdate := False;
649 FreeMemory(NetEvent.peer^.data);
650 NetEvent.peer^.data := nil;
651 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
652 Dec(NetClientCount);
654 if NetUseMaster then g_Net_Slist_Update;
655 end;
656 end;
657 end;
658 end;
661 { /// CLIENT FUNCTIONS /// }
664 procedure g_Net_Disconnect(Forced: Boolean = False);
665 begin
666 if NetMode <> NET_CLIENT then Exit;
667 if (NetHost = nil) or (NetPeer = nil) then Exit;
669 if not Forced then
670 begin
671 enet_peer_disconnect(NetPeer, NET_DISC_NONE);
673 while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
674 begin
675 if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
676 begin
677 NetPeer := nil;
678 break;
679 end;
681 if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
682 enet_packet_destroy(NetEvent.packet);
683 end;
685 if NetPeer <> nil then
686 begin
687 enet_peer_reset(NetPeer);
688 NetPeer := nil;
689 end;
690 end
691 else
692 begin
693 e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), MSG_NOTIFY);
694 if (NetEvent.data <= NET_DISC_MAX) then
695 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
696 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
697 end;
699 if NetHost <> nil then
700 begin
701 enet_host_destroy(NetHost);
702 NetHost := nil;
703 end;
704 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
706 g_Net_Cleanup;
707 e_WriteLog('NET: Disconnected', MSG_NOTIFY);
708 end;
710 procedure g_Net_Client_FlushBuffers();
711 begin
712 SendBuffer(@NetSend[CH_RELIABLE], CH_RELIABLE, NetPeer);
713 SendBuffer(@NetSend[CH_UNRELIABLE], CH_UNRELIABLE, NetPeer);
714 end;
716 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
717 var
718 I: Integer;
719 begin
720 if (Reliable) then
721 I := CH_RELIABLE
722 else
723 I := CH_UNRELIABLE;
724 e_Buffer_Write(@NetSend[I], @NetOut);
725 e_Buffer_Clear(@NetOut);
726 end;
728 function g_Net_Client_Update(): enet_size_t;
729 begin
730 Result := 0;
731 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
732 begin
733 case NetEvent.kind of
734 ENET_EVENT_TYPE_RECEIVE:
735 begin
736 g_Net_ClientMsgHandler(NetEvent.packet);
737 end;
739 ENET_EVENT_TYPE_DISCONNECT:
740 begin
741 g_Net_Disconnect(True);
742 Result := 1;
743 Exit;
744 end;
745 end;
746 end
747 end;
749 function g_Net_Client_UpdateWhileLoading(): enet_size_t;
750 begin
751 Result := 0;
752 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
753 begin
754 case NetEvent.kind of
755 ENET_EVENT_TYPE_RECEIVE:
756 begin
757 g_Net_ClientLightMsgHandler(NetEvent.packet);
758 end;
760 ENET_EVENT_TYPE_DISCONNECT:
761 begin
762 g_Net_Disconnect(True);
763 Result := 1;
764 Exit;
765 end;
766 end;
767 end;
768 end;
770 function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
771 var
772 OuterLoop: Boolean;
773 begin
774 if NetMode <> NET_NONE then
775 begin
776 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
777 Result := False;
778 Exit;
779 end;
781 Result := True;
783 g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
784 [IP, Port]));
785 if not NetInitDone then
786 begin
787 if (not g_Net_Init()) then
788 begin
789 g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
790 Result := False;
791 Exit;
792 end
793 else
794 NetInitDone := True;
795 end;
797 NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
799 if (NetHost = nil) then
800 begin
801 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
802 g_Net_Cleanup;
803 Result := False;
804 Exit;
805 end;
807 enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
808 NetAddr.port := Port;
810 NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
812 if (NetPeer = nil) then
813 begin
814 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
815 enet_host_destroy(NetHost);
816 g_Net_Cleanup;
817 Result := False;
818 Exit;
819 end;
821 OuterLoop := True;
822 while OuterLoop do
823 begin
824 while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
825 begin
826 if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
827 begin
828 g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
829 NetMode := NET_CLIENT;
830 e_Buffer_Clear(@NetOut);
831 enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
832 NetClientIP := IP;
833 NetClientPort := Port;
834 if NetDump then
835 g_Net_DumpStart();
836 Exit;
837 end;
838 end;
840 ProcessLoading();
842 e_PollInput();
844 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
845 OuterLoop := False;
846 end;
848 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
849 if NetPeer <> nil then enet_peer_reset(NetPeer);
850 if NetHost <> nil then
851 begin
852 enet_host_destroy(NetHost);
853 NetHost := nil;
854 end;
855 g_Net_Cleanup();
856 Result := False;
857 end;
859 function IpToStr(IP: LongWord): string;
860 var
861 Ptr: Pointer;
862 begin
863 Result := '';
864 Ptr := Addr(IP);
866 e_Raw_Seek(0);
867 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
868 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
869 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr)) + '.';
870 Result := Result + IntToStr(e_Raw_Read_Byte(Ptr));
871 e_Raw_Seek(0);
872 end;
874 function StrToIp(IPstr: string; var IP: LongWord): Boolean;
875 var
876 EAddr: ENetAddress;
877 begin
878 Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
879 IP := EAddr.host;
880 end;
882 function g_Net_Client_ByName(Name: string): pTNetClient;
883 var
884 a: Integer;
885 pl: TPlayer;
886 begin
887 Result := nil;
888 for a := Low(NetClients) to High(NetClients) do
889 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
890 begin
891 pl := g_Player_Get(NetClients[a].Player);
892 if pl = nil then continue;
893 if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
894 if NetClients[a].Peer <> nil then
895 begin
896 Result := @NetClients[a];
897 Exit;
898 end;
899 end;
900 end;
902 function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
903 var
904 a: Integer;
905 begin
906 Result := nil;
907 for a := Low(NetClients) to High(NetClients) do
908 if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
909 if NetClients[a].Player = PID then
910 begin
911 Result := @NetClients[a];
912 Exit;
913 end;
914 end;
916 function g_Net_ClientName_ByID(ID: Integer): string;
917 var
918 a: Integer;
919 pl: TPlayer;
920 begin
921 Result := '';
922 if ID = NET_EVERYONE then
923 Exit;
924 for a := Low(NetClients) to High(NetClients) do
925 if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
926 begin
927 pl := g_Player_Get(NetClients[a].Player);
928 if pl = nil then Exit;
929 Result := pl.Name;
930 end;
931 end;
933 procedure g_Net_SendData(Data:AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
934 var
935 P: pENetPacket;
936 F: enet_uint32;
937 dataLength: Cardinal;
938 begin
939 dataLength := Length(Data);
941 if (Reliable) then
942 F := LongWord(ENET_PACKET_FLAG_RELIABLE)
943 else
944 F := 0;
946 if (peer <> nil) then
947 begin
948 P := enet_packet_create(@Data[0], dataLength, F);
949 if not Assigned(P) then Exit;
950 enet_peer_send(peer, CH_DOWNLOAD, P);
951 end
952 else
953 begin
954 P := enet_packet_create(@Data[0], dataLength, F);
955 if not Assigned(P) then Exit;
956 enet_host_broadcast(NetHost, CH_DOWNLOAD, P);
957 end;
959 enet_host_flush(NetHost);
960 end;
962 function g_Net_Wait_Event(msgId: Word): TMemoryStream;
963 var
964 downloadEvent: ENetEvent;
965 OuterLoop: Boolean;
966 MID: Byte;
967 Ptr: Pointer;
968 msgStream: TMemoryStream;
969 begin
970 FillChar(downloadEvent, SizeOf(downloadEvent), 0);
971 msgStream := nil;
972 OuterLoop := True;
973 while OuterLoop do
974 begin
975 while (enet_host_service(NetHost, @downloadEvent, 0) > 0) do
976 begin
977 if (downloadEvent.kind = ENET_EVENT_TYPE_RECEIVE) and (downloadEvent.packet^.dataLength > 2) then
978 begin
979 Ptr := downloadEvent.packet^.data + 2; // skip length
980 MID := Byte(Ptr^);
982 if (MID = msgId) then
983 begin
984 msgStream := TMemoryStream.Create;
985 msgStream.SetSize(downloadEvent.packet^.dataLength - 2);
986 msgStream.WriteBuffer(Ptr^, downloadEvent.packet^.dataLength - 2);
987 msgStream.Seek(0, soFromBeginning);
989 OuterLoop := False;
990 enet_packet_destroy(downloadEvent.packet);
991 break;
992 end
993 else begin
994 enet_packet_destroy(downloadEvent.packet);
995 end;
996 end
997 else
998 if (downloadEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
999 begin
1000 if (downloadEvent.data <= NET_DISC_MAX) then
1001 g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' +
1002 _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + downloadEvent.data)], True);
1003 OuterLoop := False;
1004 Break;
1005 end;
1006 end;
1008 ProcessLoading();
1010 e_PollInput();
1012 if e_KeyPressed(IK_ESCAPE) or e_KeyPressed(IK_SPACE) then
1013 break;
1014 end;
1015 Result := msgStream;
1016 end;
1018 function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
1019 var
1020 I: Integer;
1021 begin
1022 Result := False;
1023 if NetBannedHosts = nil then
1024 Exit;
1025 for I := 0 to High(NetBannedHosts) do
1026 if (NetBannedHosts[I].IP = IP) and ((not Perm) or (NetBannedHosts[I].Perm)) then
1027 begin
1028 Result := True;
1029 break;
1030 end;
1031 end;
1033 procedure g_Net_BanHost(IP: LongWord; Perm: Boolean = True); overload;
1034 var
1035 I, P: Integer;
1036 begin
1037 if IP = 0 then
1038 Exit;
1039 if g_Net_IsHostBanned(IP, Perm) then
1040 Exit;
1042 P := -1;
1043 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
1044 if NetBannedHosts[I].IP = 0 then
1045 begin
1046 P := I;
1047 break;
1048 end;
1050 if P < 0 then
1051 begin
1052 SetLength(NetBannedHosts, Length(NetBannedHosts) + 1);
1053 P := High(NetBannedHosts);
1054 end;
1056 NetBannedHosts[P].IP := IP;
1057 NetBannedHosts[P].Perm := Perm;
1058 end;
1060 procedure g_Net_BanHost(IP: string; Perm: Boolean = True); overload;
1061 var
1062 a: LongWord;
1063 b: Boolean;
1064 begin
1065 b := StrToIp(IP, a);
1066 if b then
1067 g_Net_BanHost(a, Perm);
1068 end;
1070 procedure g_Net_UnbanNonPermHosts();
1071 var
1072 I: Integer;
1073 begin
1074 if NetBannedHosts = nil then
1075 Exit;
1076 for I := Low(NetBannedHosts) to High(NetBannedHosts) do
1077 if (NetBannedHosts[I].IP > 0) and not NetBannedHosts[I].Perm then
1078 begin
1079 NetBannedHosts[I].IP := 0;
1080 NetBannedHosts[I].Perm := True;
1081 end;
1082 end;
1084 function g_Net_UnbanHost(IP: string): Boolean; overload;
1085 var
1086 a: LongWord;
1087 begin
1088 Result := StrToIp(IP, a);
1089 if Result then
1090 Result := g_Net_UnbanHost(a);
1091 end;
1093 function g_Net_UnbanHost(IP: LongWord): Boolean; overload;
1094 var
1095 I: Integer;
1096 begin
1097 Result := False;
1098 if IP = 0 then
1099 Exit;
1100 if NetBannedHosts = nil then
1101 Exit;
1102 for I := 0 to High(NetBannedHosts) do
1103 if NetBannedHosts[I].IP = IP then
1104 begin
1105 NetBannedHosts[I].IP := 0;
1106 NetBannedHosts[I].Perm := True;
1107 Result := True;
1108 // no break here to clear all bans of this host, perm and non-perm
1109 end;
1110 end;
1112 procedure g_Net_SaveBanList();
1113 var
1114 F: TextFile;
1115 I: Integer;
1116 begin
1117 Assign(F, DataDir + BANLIST_FILENAME);
1118 Rewrite(F);
1119 if NetBannedHosts <> nil then
1120 for I := 0 to High(NetBannedHosts) do
1121 if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then
1122 Writeln(F, IpToStr(NetBannedHosts[I].IP));
1123 CloseFile(F);
1124 end;
1126 procedure g_Net_DumpStart();
1127 begin
1128 if NetMode = NET_SERVER then
1129 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_server')
1130 else
1131 NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_client');
1132 end;
1134 procedure g_Net_DumpSendBuffer();
1135 begin
1136 writeInt(NetDumpFile, gTime);
1137 writeInt(NetDumpFile, LongWord(NetOut.WritePos));
1138 writeInt(NetDumpFile, Byte(1));
1139 NetDumpFile.WriteBuffer(NetOut.Data[0], NetOut.WritePos);
1140 end;
1142 procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord);
1143 begin
1144 if (Buf = nil) or (Len = 0) then Exit;
1145 writeInt(NetDumpFile, gTime);
1146 writeInt(NetDumpFile, Len);
1147 writeInt(NetDumpFile, Byte(0));
1148 NetDumpFile.WriteBuffer(Buf^, Len);
1149 end;
1151 procedure g_Net_DumpEnd();
1152 begin
1153 NetDumpFile.Free();
1154 NetDumpFile := nil;
1155 end;
1157 end.