/
basefunctions_network.bmx
executable file
·1898 lines (1585 loc) · 50.2 KB
/
basefunctions_network.bmx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
SuperStrict
Import pub.enet
Import Brl.Map
Import Brl.Stream
Import Brl.Retro
'for direct udp messages
?bmxng
Import "external/bnetex/bnetex_ng.bmx"
?not bmxng
Import "external/bnetex/bnetex.bmx"
?
Import "Dig/base.util.event.bmx"
Import "Dig/base.util.logger.bmx"
Const NET_PACKET_RELIABLE:int = 1
Const NET_PACKET_HEADER:int = 1317
'=== MESSAGE STATES ===
Const NET_STATE_ALL:int = 0
Const NET_STATE_CREATED:int = 1
Const NET_STATE_MODIFIED:int = 2
Const NET_STATE_CLOSED:int = 3
Const NET_STATE_SYNCED:int = 4
Const NET_STATE_MESSAGE:int = 5
Const NET_STATE_FOREVER:int = 6
Const NET_STATE_ZOMBIE:int =-1
'=== PACKET SLOT TYPES ===
Const NET_TYPE_STRING:int = 1
Const NET_TYPE_INT:int = 2
Const NET_TYPE_UINT8:int = 3
Const NET_TYPE_UINT16:int = 4
Const NET_TYPE_FLOAT16:int = 5
Const NET_TYPE_FLOAT32:int = 6
Const NET_TYPE_DOUBLE:int = 7
Const NET_TYPE_LONG:int = 8
'=== NETWORK PACKET TYPES ===
Const NET_CONNECT:Int = 1 ' ALL: Connect Attempt
Const NET_DISCONNECT:Int = 2 ' ALL: DISCONNECT
Const NET_PINGREQUEST:Int = 3 ' Ping
Const NET_PINGRESPONSE:Int = 4 ' Pong
Const NET_JOINREQUEST:Int = 5 ' ALL: Join Attempt
Const NET_JOINRESPONSE:Int = 6 ' ALL: Join Attempt - Response
Const NET_LEAVEGAME:Int = 7
Const NET_LEAVEGAMEREQUEST:Int = 8
Const NET_LEAVEGAMERESPONSE:Int = 9
CONST NET_PLAYERLEFT:int = 10
CONST NET_PLAYERJOINED:int = 11
Const NET_ANNOUNCEGAME:Int = 12 ' SERVER: Announce a game
rem
'done in bnetex.bmx already
Extern "OS"
?Win32
Const FIONREAD : Int = $4004667F
Const SOL_SOCKET_ : Int = $FFFF
Const SO_BROADCAST_ : Short = $20
Const SO_SNDBUF_ : Short = $1001
Const SO_RCVBUF_ : Short = $1002
?MacOS
Const FIONREAD : Int = $4004667F
Const SOL_SOCKET_ : Int = $FFFF
Const SO_BROADCAST_ : Short = $20
Const SO_SNDBUF_ : Short = $1001
Const SO_RCVBUF_ : Short = $1002
?Linux
Const FIONREAD : Int = $0000541B
Const SOL_SOCKET_ : Int = 1
Const SO_BROADCAST_ : Short = 6
Const SO_SNDBUF_ : Short = 7
Const SO_RCVBUF_ : Short = 8
?Win32
Function ioctl_:Int(Socket:Int, Command:Int, Arguments:Byte Ptr) = "ioctlsocket@12"
Function inet_addr_:Int(Address$z) = "inet_addr@4"
Function inet_ntoa_:Byte Ptr(Adress:Int) = "inet_ntoa@4"
Function getsockname_:Int(Socket:Int, Name:Byte Ptr, NameLen:Int Ptr) = "getsockname@12"
Function GetCurrentProcessId:Int() = "GetCurrentProcessId@0"
?MacOS
Function ioctl_:Int(Socket:Int, Command:Int, Arguments:Byte Ptr) = "ioctl"
Function inet_addr_:Int(Address$z) = "inet_addr"
Function inet_ntoa_:Byte Ptr(Adress:Int) = "inet_ntoa"
Function getsockname_:Int(Socket:Int, Name:Byte Ptr, NameLen:Int Ptr) = "getsockname"
Function GetCurrentProcessId:Int() = "getpid"
?Linux
Function ioctl_:Int(Socket:Int, Command:Int, Arguments:Byte Ptr) = "ioctl"
Function inet_addr_:Int(Address$z) = "inet_addr"
Function inet_ntoa_:Byte Ptr(Adress:Int) = "inet_ntoa"
Function getsockname_:Int(Socket:Int, Name:Byte Ptr, NameLen:Int Ptr) = "getsockname"
Function GetCurrentProcessId:Int() = "getpid"
?
End Extern
EndRem
'all players wo take part in a game
Type TNetworkClient extends TNetworkConnection
Global list:TList = New TList
Global maxClients:Int = 1
Field name:String = "Mr.X"
Field server:TNetworkServer
Field link:TLink
Field playerID:int =-1
Field playerName:string = "playerName"
Field callback(client:TNetworkClient,evType:Int,networkObject:TNetworkObject)
Field latency:Int
Method GetPlayerID:int()
return playerID
End Method
Method Free()
If link
link.remove()
link=Null
EndIf
Super.Free()
End Method
Function Find:TNetworkClient(ip:Int,port:Int)
Local client:TNetworkClient
For client=EachIn list
If client.ip=ip And client.port=port then Return client
Next
client = New TNetworkClient
client.ip = ip
client.port = port
client.link = list.addlast(client)
Return client
EndFunction
Function Create:TNetworkClient(port:int, playerID:int=-1, asServer:int=0)
local portRange:int = 20
Local addr:Byte Ptr
Local client:TNetworkClient=New TNetworkClient
client.playerID = playerID
If portRange<=1
client.ip = ENET_HOST_ANY
client.port = port
addr = enet_address_create( client.ip,client.port )
?bmxng
client.enethost = enet_host_create( addr,size_t(maxClients),0,0, 0)
?not bmxng
client.enethost = enet_host_create( addr,maxClients,0,0 )
?
enet_address_destroy(addr)
If Not client.enethost Return Null
client.link = list.addlast(client)
Else
If port=0 then port=4544
For Local n:Int=port To port+portrange-1
addr = enet_address_create( ENET_HOST_ANY,n )
?bmxng
client.enethost = enet_host_create( addr,size_t(maxClients),0,0, 0 )
?not bmxng
client.enethost = enet_host_create( addr,maxClients,0,0 )
?
enet_address_destroy(addr)
If client.enethost then client.port = port;exit
Next
EndIf
Return client
End Function
Method Disconnect(force:Int = False)
Const disconnecttimeout:Int=10000
If Not Self.enethost then RuntimeError "Can't update a remote server."
If Server
If force
?bmxng
enet_peer_disconnect(server.enetpeer, 0)
?not bmxng
enet_peer_reset(server.enetpeer)
?
Else
SendEvent(NET_LEAVEGAMEREQUEST,NET_PACKET_RELIABLE)
Local ev:ENetEvent=New ENetEvent
Local start:Long = Time.GetTimeGone()
Repeat
If Time.GetTimeGone() - start > disconnecttimeout then Exit
if not enet_host_service(self.enethost,ev,100) then Exit
Select ev.event()
Case ENET_EVENT_TYPE_RECEIVE
If ev.packet()
Local packet:TNetworkPacket
?bmxng
Local size:Int=bmx_enet_packet_size(ev.packet())
?not bmxng
Local size:Int=enet_packet_size(ev.packet())
?
Local data:Byte[size]
If size
packet=New TNetworkPacket
?bmxng
packet._bank.resize(Size_T(size))
MemCopy(packet._bank.buf(),bmx_enet_packet_data(ev.packet()),Size_T(size))
?not bmxng
packet._bank.resize(size)
MemCopy(packet._bank.buf(),enet_packet_data(ev.packet()),size)
?
EndIf
local obj:TNetworkObject = TNetworkObject.FromPacket(packet)
if obj.evType = NET_LEAVEGAMERESPONSE then exit
EndIf
EndSelect
Forever
?bmxng
enet_peer_disconnect(server.enetpeer, 0)
?not bmxng
enet_peer_disconnect(server.enetpeer)
?
EndIf
server=Null
EndIf
joined=False
print ""
print "- - - - - - - - - -"
print "| DISCONNECTED !! |"
print "- - - - - - - - - -"
print ""
End Method
Method Connect:int(ip:Int, port:Int, timeout:int = 0)
'host ueberschreiben ?
' If enethost then Return 'we are a server or alreay connected
' enethost = enet_host_create( Null,32,0,0 )
If Not enethost then RuntimeError "Remote client cannot connect to server."
if Server then self.disconnect()
server = New TNetworkServer
server.ip = ip
server.port = port
Local addr:Byte Ptr = enet_address_create(server.ip, server.port)
?bmxng
server.enetpeer = enet_host_connect(enethost, addr, size_t(channels), 0)
?not bmxng
server.enetpeer = enet_host_connect(enethost, addr, channels)
?
enet_address_destroy( addr )
If server.enetpeer = Null
print "connection failed"
server = Null
Return 0
EndIf
return 1
End Method
Method SendEvent:Int(_event:int=Null,flags:Int=0,channel:Int=0)
if _event = null then print "null event"
local obj:TNetworkObject = TNetworkObject.Create(_event )
self.send( obj, flags)
End Method
'sends a packet from this client to the connected server
Method Send:Int(NetworkObject:TNetworkObject=Null,flags:Int=0,channel:Int=0)
if not server
print "Trying to send packet without server."
'we return TRUE so the package seems to get sent successful.
return True
endif
if not NetworkObject then return 0
local packet:TNetworkPacket = NetworkObject.ToPacket()
if not packet then return 0
If Not connected then Return 0
If packet and packet._bank.size()=0 then packet=Null
Local result:Int
If packet
Local data:Byte[] = New Byte[packet._bank.size()]
MemCopy(Varptr data[0],packet._bank.buf(),packet._bank.size())
?bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,size_t(data.length),uint(flags))
'send to this peer / connection
result = (enet_peer_send(server.enetpeer,Byte(channel),enetpacket)=0)
?not bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,data.length,flags)
'send to this peer / connection
result = (enet_peer_send(server.enetpeer,channel,enetpacket)=0)
?
endif
return result
End Method
Method EvaluateEvent(evType:Int,packet:TNetworkPacket,enetpeer:Byte Ptr)
local response:TNetworkObject = TNetworkObject.fromPacket(packet)
if response = null then response = TNetworkObject.Create(0)
if evType = 0 then evType = response.evType
?not bmxng
enet_peer_address( enetpeer , response.senderIP , response.senderPort )
?bmxng
Throw "implement enet_peer_address"
?
'print "<-- client receives packet from="+dottedIP(response.senderIP)+", evType="+evType
Select evType
Case NET_CONNECT
if not joined
TLogger.Log("Network.EvaluateEvent()", "New client connects.", LOG_DEBUG | LOG_NETWORK)
Self.connected = 1
local obj:TNetworkObject = TNetworkObject.Create(NET_JOINREQUEST )
obj.setString(1, playerName)
self.Send(obj)
endif
Case NET_JOINRESPONSE
'got join - if ok then also set playerID
joined = response.getInt(1)
local playerID:int = response.getInt(2)
If not joined OR playerID > self.maxClients
Disconnect()
else
self.playerID = Max(0,playerID)
endif
TLogger.Log("Network.EvaluateEvent()", "Got join response. Set playerID to " +self.playerID, LOG_DEBUG | LOG_NETWORK)
Case NET_PINGREQUEST
'change ev type from ping request to response
response.evType = NET_PINGRESPONSE
Send(response)
Case NET_PINGRESPONSE
latency = Time.GetTimeGone() - response.getLong(1)
response.SetInt(2, response.senderIP)
response.SetInt(3, response.senderPort)
If enetpeer=pingpeer
?bmxng
enet_peer_disconnect(pingpeer, 0)
?not bmxng
enet_peer_disconnect(pingpeer)
?
pingpeer=Null
EndIf
EndSelect
'run extension callback
If callback then callback(Self,evType, response)
End Method
Method Ping:Int(ip:Int=0,port:Int=0)
local obj:TNetworkObject = TNetworkObject.Create(NET_PINGREQUEST)
obj.SetLong(1, Time.GetTimeGone())
Return Send(obj)
End Method
End Type
Type TNetworkServer Extends TNetworkConnection
Field clients:TList=New TList
Field clientmap:TMap = CreateMap()
Field callback(server:TNetworkServer,client:TNetworkclient,id:Int, networkObject:TNetworkObject)
?bmxng
Field bannedips:String[]
?not bmxng
Field bannedips:Int[]
?
Function Create:TNetworkServer(port:Int=0,portRange:Int=40)
Local addr:Byte Ptr
local server:TNetworkServer=New TNetworkServer
server.ip = ENET_HOST_ANY
If portrange <= 1
addr = enet_address_create( server.ip,server.port )
?bmxng
server.enethost = enet_host_create( addr,32,0,0, 0)
?not bmxng
server.enethost = enet_host_create( addr,32,0,0 )
?
enet_address_destroy(addr)
Else
If port=0 then port=4544
For Local n:Int=port To port+portrange-1
addr = enet_address_create( ENET_HOST_ANY,n )
?bmxng
server.enethost = enet_host_create( addr,64,0,0, 0 )
?not bmxng
server.enethost = enet_host_create( addr,64,0,0 )
?
enet_address_destroy(addr)
If server.enethost
server.port = n
Exit
Endif
Next
EndIf
If Not server.enethost then Return Null
Return server
End Function
Method FindClientByName:TNetworkClient(name:String)
Return TNetworkClient( clientmap.valueforkey(name) )
End Method
Method FindClientByPeer:TNetworkclient(peer:Byte Ptr)
Local client:TNetworkclient
For client=EachIn clients
If client.enetpeer = peer then Return client
Next
return Null
End Method
Method FindClient:TNetworkClient(ip:Int,port:Int)
Local client:TNetworkClient
For client=EachIn clients
If client.ip = ip And client.port = port then Return client
Next
return Null
End Method
Method EvaluateEvent(evType:Int,packet:TNetworkPacket,enetpeer:Byte Ptr)
Local client:TNetworkClient=FindClient(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
local response:TNetworkObject = TNetworkObject.fromPacket(packet)
'should we only act as relay (client broadcasting)
local toRelay:int = floor(response.evType / 10000)
if toRelay
response.evType :mod 10000
'print "Server got relay event:" + response.evType
'at the moment broadcasts are reliable
'self.broadcast(response, null, NET_PACKET_RELIABLE)
self.broadcast(response, client, NET_PACKET_RELIABLE)
return
endif
Local answer:TNetworkObject
?not bmxng
enet_peer_address( enetpeer , response.senderIP , response.senderPort )
?bmxng
Throw "implement enet_peer_address"
?
' print "<-- server receives packet from="+dottedIP(response.senderIP)+", evType="+evType
if evType = 0 then evType = response.evType
Select evType
Case NET_PINGRESPONSE
client.latency = Time.GetTimeGone() - response.getInt(1)
Case NET_LEAVEGAMEREQUEST
If client
client.SendEvent(NET_LEAVEGAMERESPONSE, NET_PACKET_RELIABLE)
answer = TNetworkObject.Create(NET_PLAYERLEFT)
answer.SetInt(1, client.playerID)
answer.setString(2, client.name)
Broadcast(answer, null, NET_PACKET_RELIABLE)
clients.remove(client)
If clientmap.valueforkey(client.name)=client then clientmap.remove(client.name)
EndIf
Case NET_JOINREQUEST
If client
Disconnect(client,1)
answer = TNetworkObject.Create(NET_PLAYERLEFT)
answer.SetInt(1, client.playerID)
Broadcast(answer)
EndIf
client = TNetworkClient.Find(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
client.enetpeer=enetpeer
'check if IP is banned/not allowed to join
If IPBanned(client.ip)
Disconnect(client,0)
Return
Endif
'handle join
client.name = response.GetString(1)
'duplicate name "boon" => "boon (1)"
If FindClientByName(client.name)
local addNum:int=0
while FindClientByName(client.name+" ("+addNum+")")
addNum:+1
Wend
client.name = client.name + " ("+addNum+")"
TLogger.Log("Network.EvaluateEvent()", "Handle join: clients name already taken, renamed to "+client.name, LOG_DEBUG | LOG_NETWORK)
endif
If not FindClientByName(client.name)
clientmap.insert(client.name,client)
clients.AddLast(client)
client.playerID = clients.count()
'answer the clients join request, inform about the
'playerID the client has to assign
answer = TNetworkObject.Create(NET_JOINRESPONSE)
answer.SetInt(1, 1)
answer.SetInt(2, clients.count()) 'change to GetSlot() or so
SendToClient(client, answer,NET_PACKET_RELIABLE)
TLogger.Log("Network.EvaluateEvent()", "Handle join: send back accepted join packet", LOG_DEBUG | LOG_NETWORK)
'send peer data
'Tell everyone new client joined
answer = TNetworkObject.Create(NET_PLAYERJOINED)
answer.SetInt(1, clients.count()) 'change to GetSlot() or so
answer.setString(2, client.name)
'inform callback about playerjoined
If callback then callback(Self, client, evType, answer)
Broadcast(answer, null, NET_PACKET_RELIABLE)
Else
TLogger.Log("Network.EvaluateEvent()", "Handle join: client cannot join, no name already taken. Rename not possible.", LOG_DEBUG | LOG_NETWORK)
answer = TNetworkObject.Create(NET_JOINRESPONSE)
answer.SetInt(1, 0) 'no, you can't joint
answer.SetInt(2, 1) 'reason: name already taken
client.Send(answer,NET_PACKET_RELIABLE)
Return
EndIf
Case NET_CONNECT
Return
Case NET_DISCONNECT
If client
clients.remove(client)
If clientmap.valueforkey(client.name)=client then clientmap.remove(client.name)
If Not client.enethost then client.free()
EndIf
Case NET_PINGREQUEST
If Not client
client = New TNetworkClient
client.enetpeer = enetpeer
EndIf
response.evType = NET_PINGRESPONSE
SendToClient(client, response)
response.evType = NET_PINGREQUEST
'Return
EndSelect
If callback then callback(Self,client,evType, response )
End Method
Method BroadcastEvent:Int(_event:int=Null, exceptClient:TNetworkClient=null, flags:Int=0,channel:Int=0)
self.Broadcast(CreateNetworkObject(_event), exceptClient, flags, channel)
End Method
Method Broadcast:Int(NetworkObject:TNetworkObject=Null, exceptClient:TNetworkClient=null, flags:Int=0,channel:Int=0)
if not NetworkObject then return 0
local packet:TNetworkPacket = NetworkObject.ToPacket()
Local result:Int=1
For Local client:TNetworkClient=EachIn clients
if client <> exceptClient
' if not client.send(NetworkObject, flags, channel) then result = 0
if Not SendPacketToClient(client, packet,flags,channel) then result=0
endif
Next
Return result
End Method
Method Send:Int(NetworkObject:TNetworkObject=Null,flags:Int=0,channel:Int=0)
if not NetworkObject then return 0
local packet:TNetworkPacket = NetworkObject.ToPacket()
if not packet then return 0
If packet and packet._bank.size()=0 then packet=Null
Local result:Int
If packet
Local data:Byte[] = New Byte[packet._bank.size()]
MemCopy(Varptr data[0],packet._bank.buf(),packet._bank.size())
?bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,size_t(data.length),uint(flags))
'send to this peer / connection
result = (enet_peer_send(self.enetpeer,Byte(channel),enetpacket)=0)
?not bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,data.length,flags)
'send to this peer / connection
result = (enet_peer_send(self.enetpeer,channel,enetpacket)=0)
?
endif
return result
End Method
'send packet - so for broadcast we dont need "topacket" for each client
Method SendPacketToClient:Int(client:TNetworkClient, packet:TNetworkPacket=Null,flags:Int=0,channel:Int=0)
if not packet then return 0
If packet and packet._bank.size()=0 then packet=Null
If Not client.enetpeer then RuntimeError "Can't send to local client."
Local result:Int
If packet
Local data:Byte[] = New Byte[packet._bank.size()]
MemCopy(Varptr data[0],packet._bank.buf(),packet._bank.size())
?bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,size_t(data.length),uint(flags))
'send to this peer / connection
result = (enet_peer_send(client.enetpeer,Byte(channel),enetpacket)=0)
?not bmxng
Local enetpacket:Byte Ptr = enet_packet_create(data,data.length,flags)
'send to this peer / connection
result = (enet_peer_send(client.enetpeer,channel,enetpacket)=0)
?
endif
return result
End Method
Method SendToClient:Int(client:TNetworkClient, NetworkObject:TNetworkObject=Null,flags:Int=0,channel:Int=0)
if not NetworkObject then return 0
local packet:TNetworkPacket = NetworkObject.ToPacket()
return self.SendPacketToClient(client, packet, flags, channel)
End Method
Method Disconnect(client:TNetworkClient,force:Int=False)
If client.enetpeer
If force
?not bmxng
enet_peer_reset(client.enetpeer)
?bmxng
Throw "implement enet_peer_reset"
?
Else
?bmxng
enet_peer_disconnect(client.enetpeer, 0)
?not bmxng
enet_peer_disconnect(client.enetpeer)
?
EndIf
clients.remove(client)
If Not client.enethost then client.link.remove()
If clientmap.valueforkey(client.name)=client then clientmap.remove(client.name)
client.enetpeer=Null
EndIf
End Method
?bmxng
Method BanIP(ip:String)
?not bmxng
Method BanIP(ip:Int)
?
bannedips=bannedips[..bannedips.length+1]
bannedips[bannedips.length-1]=ip
End Method
?bmxng
Method IPBanned:Int(ip:String)
?not bmxng
Method IPBanned:Int(ip:Int)
?
For Local n:Int=0 To bannedips.length-1
If ip=bannedips[n] Return True
Next
Return False
End Method
Method Kick(client:TNetworkClient)
BanIP(client.ip)
Disconnect(client)
End Method
EndType
Function CreateNetworkObject:TNetworkObject(evType:int)
return TNetworkObject.Create(evType)
End Function
Type TNetworkPacket Extends TBankStream
field ip:int
field port:int
Method New()
_bank=New TBank
End Method
EndType
'base of all networkevents...
'added to the base networking functions are all methods to send and receive data from clients
'including playerposition, programmesending, changes of elevatorpositions etc.
Type TDigNetwork
Global localFallbackIP:int = 0
Global localPort:short = 4544
' only for lan (up to now) - announcements
Global infoPort:short = 4543
Global maxPlayers:int = 4
field fallbackip:String = "192.168.0.3"
'=== to interact with clients/host ===
Field infoStream:TUDPStream = Null
Field callbackClient(client:TNetworkClient,evType:Int,networkObject:TNetworkObject)
Field client:TNetworkClient = Null
Field callbackServer(server:TNetworkServer,client:TNetworkclient,id:Int,networkObject:TNetworkObject)
Field server:TNetworkServer = Null
'=== runtime variables ===
' Are you hosting or joining?
Field isServer:int = False
' Are you connected?
Field IsConnected:int = False
' Are we already playing?
Field inGame:int = False
' ip for internet
Field OnlineIP:String = ""
' int version of ip for internet
Field intOnlineIP:Int = 0
'=== game announcements ===
Field announceEnabled:int = 0
' title used when announcing
Field announceTitle:string = "unknown"
' Main Announcement Timer
Field announceTime:Long = -1
' Main Announcement Timer time
Field announceTimer:Int = 750
Field announceToLan:int = 1
' Ping Timer
Field pingTime:Long = 0
' Ping Timer
Field pingTimer:Int = 4000
' Update Timer
Field UpdateTime:Long
' Main Update Timer
Field MainSyncLong:Int
Field LastOnlineRequestTimer:Long = 0
'time until next action (getlist, announce...)
Field LastOnlineRequestTime:Int = 10000
Field LastOnlineHashCode:String = ""
' // Networking Constants
Field ChatSpamTime:Long = 0
Global eventKey_OnReceiveAnnounceGame:TEventKey = EventManager.GetEventKey("network.onReceiveAnnounceGame", True)
Global eventKey_OnCreateServer:TEventKey = EventManager.GetEventKey("network.onCreateServer", True)
Global eventKey_OnStopServer:TEventKey = EventManager.GetEventKey("network.onStopServer", True)
Global eventKey_OnConnectToServer:TEventKey = EventManager.GetEventKey("network.onConnectToServer", True)
Method New()
TNetworkClient.maxClients = self.maxPlayers
End Method
Method InitInfoStream()
if infoStream then return
If infoStream = Null Then infoStream = new TUDPStream
if infoStream = null then RuntimeError "Couldn't create UDP stream for LAN game announcements"
infoStream.Init()
infoStream.SetLocalPort(self.infoPort)
infoStream.SetRemotePort(self.infoPort)
End Method
Method StopAnnouncing:int()
self.announceEnabled = false
End Method
Method StartAnnouncing(title:string, toLan:int = 1)
self.InitInfoStream()
self.announceEnabled = true
self.announceTitle = title
self.announceTime = Time.GetTimeGone()
self.announceToLan = toLan
End Method
Method FindGames()
if inGame then return
if not infoStream
TLogger.Log("Network.FindGames()", "Initialized info stream.", LOG_DEBUG | LOG_NETWORK)
self.InitInfoStream()
endif
local packet:TNetworkPacket = self.ReceiveInfoPackets()
if packet
local obj:TNetworkObject = TNetworkObject.FromPacket(packet)
if not obj then return
if obj.evType = NET_ANNOUNCEGAME
'only emit event for clients (not the server)
if not server
local evData:TData = new TData
evData.AddNumber("slotsUsed", obj.getInt(1))
evData.AddNumber("slotsMax", obj.getInt(2))
'could differ from senderIP
evData.AddNumber("hostIP", obj.getInt(3))
'differs from senderPort (info channel)
evData.AddNumber("hostPort", obj.getInt(4))
evData.AddString("hostName", obj.getString(5))
evData.AddString("gameTitle", obj.getString(6))
TEventBase.Create(eventKey_onReceiveAnnounceGame, evData).trigger()
'print "...announce from: "+ GetDottedIP(obj.getInt(3))
endif
endif
endif
End Method
Method DisconnectFromServer()
if server then server.Disconnect(client)
if client then client.Disconnect(True)
End Method
Method StartServer:int( )
if not server then server = TNetworkServer.Create(localPort, 20)
if server
self.isServer = true
self.server.callback = self.callbackServer
TEventBase.Create(eventKey_onCreateServer, new TData.AddNumber("successful", true)).trigger()
TLogger.Log("Network.StartServer()", "created server : "+GetDottedIP(GetMyIP())+":"+server.port, LOG_DEBUG | LOG_NETWORK)
return true
else
TEventBase.Create(eventKey_onCreateServer, new TData.AddNumber("successful", false)).trigger()
return false
endif
End Method
Method StopServer:int( )
self.isServer = false
server.free()
client.free()
self.server = null
self.client = null
TEventBase.Create(eventKey_onStopServer, null).trigger()
End Method
Method GetMyIP:int()
local MyIP:int = GetHostIP("")
If MyIP = 0 then MyIP = self.localFallbackIP
return MyIP
End Method
Function GetDottedIP:string(ip:int)
'results differ with DottedIP() !!
'return DottedIP(ip)
return (ip Shr 24)+"."+(ip Shr 16 & 255)+"."+(ip Shr 8 & 255 )+"."+(ip & 255)
End Function
Method ConnectToLocalServer:int()
if not client then client = TNetworkClient.Create(localPort, 20)
self.client.callback = self.callbackClient
self.isConnected = client.Connect(getMyIP(), localPort)
return self.isConnected
End Method
Method ConnectToServer:int( ip:int, port:int )
if not client then client = TNetworkClient.Create(localPort, 20)
self.client.callback = self.callbackClient
self.isConnected = client.Connect(ip, port)
TEventBase.Create(eventKey_onConnectToServer, new TData.AddNumber("successful", isConnected)).trigger()
TLogger.Log("Network.ConnectToServer()", "connect to "+GetDottedIP(ip)+":"+port, LOG_DEBUG | LOG_NETWORK)
return self.isConnected
End Method
'client wants to broadcast - so we send a relaypacket to server
Method BroadcastNetworkObject(obj:TNetworkObject, flags:int = 0)
if not client then return
'add 10000 so we see its for relaying
obj.evType = obj.evType + 10000
'client sends to server
client.send(obj, flags)
End Method
Method Update:int()
if self.isServer then self.SendGameAnnouncement()
self.FindGames()
if self.server then self.server.Update()
if self.client
if self.pingTime < Time.GetTimeGone()
self.client.Ping()
self.pingTime = Time.GetTimeGone() + self.pingTimer
endif
self.client.Update()
endif
End Method
Function URLEncode:String(txt:String)
txt=Replace(txt,"%","%25")
txt=Replace(txt,"+","%2B")
txt=Replace(txt," ","%20")
txt=Replace(txt,"@","%40")
txt=Replace(txt,"ß","%DF")
txt=Replace(txt,"?","%3F")
txt=Replace(txt,"=","%3D")
txt=Replace(txt,")","%29")
txt=Replace(txt,"(","%28")
txt=Replace(txt,"/","%2F")
txt=Replace(txt,"&","%26")
txt=Replace(txt,"$","%24")
txt=Replace(txt,"§","%A7")
txt=Replace(txt,Chr(34),"%22")
txt=Replace(txt,"!","%21")
txt=Replace(txt,"^","%5E")
txt=Replace(txt,"#","%23")
txt=Replace(txt,"<","%3C")
txt=Replace(txt,">","%3E")
txt=Replace(txt,"{","%7B")
txt=Replace(txt,"}","%7D")
txt=Replace(txt,"[","%5B")
txt=Replace(txt,"]","%5D")
Return txt
End Function
Function URLDecode:String(txt:String)
txt=Replace(txt,"%25","%")
txt=Replace(txt,"%2B","+")
txt=Replace(txt,"%20"," ")
txt=Replace(txt,"%40","@")
txt=Replace(txt,"%DF","ß")
txt=Replace(txt,"%3F","?")
txt=Replace(txt,"%3D","=")
txt=Replace(txt,"%29",")")
txt=Replace(txt,"%28","(")
txt=Replace(txt,"%2F","/")
txt=Replace(txt,"%26","&")
txt=Replace(txt,"%24","$")
txt=Replace(txt,"%A7","§")
txt=Replace(txt,"%22",Chr(34))
txt=Replace(txt,"%21","!")
txt=Replace(txt,"%5E","^")
txt=Replace(txt,"%23","#")
txt=Replace(txt,"%3C","<")
txt=Replace(txt,"%3E",">")
txt=Replace(txt,"%7B","{")
txt=Replace(txt,"%7D","}")
txt=Replace(txt,"%5B","[")
txt=Replace(txt,"%5D","]")
Return txt
End Function
Function GetHostIP:Int(HostName:String)
?bmxng
return DottedIPToInt(HostIps("localhost")[0])
?not bmxng
Local Addresses:Byte Ptr Ptr, AddressType:Int, AddressLength:Int
Local PAddress:Byte Ptr, Address:Int
Addresses = gethostbyname_(HostName, AddressType, AddressLength)
If (Not Addresses) Or AddressType <> AF_INET_ Or AddressLength <> 4 Then Return 0
If Addresses[0] Then
PAddress = Addresses[0]
Address = (PAddress[0] Shl 24) | (PAddress[1] Shl 16) | ..
(PAddress[2] Shl 8) | PAddress[3]
Return Address
Else
Return 0