Segmentation fault

Started by wombats, June 08, 2020, 14:23:15

Previous topic - Next topic

wombats

Hi,

I'm trying to get an old project of mine to work in BlitzMax NG. It uses this networking code by JoshK. I had to make some changes to get it to compile with the new ENet in NG:

Private

Const ENET_PACKET_FLAG_UNSEQUENCED:Int=2
Const SERVERUPDATEFREQUENCY:Int=4*1000*60

Function enet_host_port:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[1]
Local port:Int=(Short Ptr peer)[4]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return port
EndFunction

Function enet_host_ip:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[1]
Local port:Int=(Short Ptr peer)[4]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return ip
EndFunction

Function enet_peer_port:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[3]
Local port:Int=(Short Ptr peer)[8]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return port
EndFunction

Function enet_peer_ip:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[3]
Local port:Int=(Short Ptr peer)[8]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return ip
EndFunction

Public

Type TNetworkNode

Field port:Int
Field ip:Int
Field enethost:Byte Ptr
Field enetpeer:Byte Ptr

Method Delete()
Free()
EndMethod

Method Free()
If enethost
enet_host_destroy(enethost)
enethost=Null
EndIf
EndMethod

Method Update()

Local ip:Int,port:Int,client:TClient,ev:ENetEvent=New ENetEvent,id:Byte,packet:TPacket

If Not Self.enethost RuntimeError "Can't update a remote server."
Repeat
If enet_host_service(Self.enethost,ev,0)

Select ev.event

Case ENET_EVENT_TYPE_CONNECT
id=NETWORK_CONNECT

Case ENET_EVENT_TYPE_DISCONNECT
id=NETWORK_DISCONNECT

Case ENET_EVENT_TYPE_RECEIVE
Local size:Int=bmx_enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,bmx_enet_packet_data(ev.packet),1)
If size>1
packet=New TPacket
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),bmx_enet_packet_data(ev.packet)+1,size-1)
EndIf

Default
Continue

EndSelect

EvaluateEvent(id,packet,ev.peer)

Else
Exit
EndIf

If enethost=Null Exit

Forever

EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Abstract

EndType

'Public


Const NETWORK_CONNECT:Int=1
Const NETWORK_DISCONNECT:Int=2
Const NETWORK_PINGREQUEST:Int=3
Const NETWORK_PINGRESPONSE:Int=4
Const NETWORK_JOINREQUEST:Int=5
Const NETWORK_JOINRESPONSE:Int=6
Const NETWORK_CHAT:Int=7
Const NETWORK_LEAVEGAME:Int=8
Const NETWORK_CHANGENAMEREQUEST:Int=9
Const NETWORK_CHANGENAMERESPONSE:Int=10
Const NETWORK_PLAYERJOINED:Int=11
Const NETWORK_PLAYERLEFT:Int=12
Const NETWORK_LEAVEGAMEREQUEST:Int=13
Const NETWORK_LEAVEGAMERESPONSE:Int=14
Const NETWORK_PLAYERCHANGEDNAME:Int=15

Const SEND_RELIABLE:Int=ENET_PACKET_FLAG_RELIABLE
Const SEND_UNSEQUENCED:Int=ENET_PACKET_FLAG_UNSEQUENCED

Type TServer Extends TNetworkNode

Field clients:TList=New TList
Field callback(server:TServer,client:TClient,id:Int,packet:TPacket)
Field bannedips:Int[]
Field clientmap:TMap=New TMap
Field url:String
Field lastrefreshtime:Int

Method Free()
If url
Remove()
url=""
EndIf
Super.Free()
EndMethod

Function EncodeURL:String(t:String)
Local newString:String
Local i:Int,c:String,asciival:Int,hexstr:String,newhexstr:String
For i = 0 To Len(t)
c:String = t[i..i+1]
asciival = Asc(c)
If asciival > 32 And asciival < 123
' handle replacing the special set of chars
c = Replace(c,"'","%27")
c = Replace(c,"%","%25")
c = Replace(c,"<","%3C")
c = Replace(c,">","%3E")
c = Replace(c,"\","%5C")
c = Replace(c,"^","%5E")
c = Replace(c,"[","%5B")
c = Replace(c,"]","%5D")
c = Replace(c,"+","%2B")
c = Replace(c,"$","%24")
c = Replace(c,",","%2C")
c = Replace(c,"@","%40")
c = Replace(c,":","%3A")
c = Replace(c,";","%3B")
c = Replace(c,"/","%2F")
c = Replace(c,"!","%21")
c = Replace(c,"#","%23")
c = Replace(c,"?","%3F")
c = Replace(c,"=","%3D")
c = Replace(c,"&","%26")
newString:+c
Else
hexstr$ = Hex(asciival)
newhexstr$ = "%" + hexstr[Len(hexstr)-2..Len(hexstr)]
newstring:+newhexstr
EndIf
Next

newstring = newstring[..Len(newstring)-3]
Return newstring
EndFunction

Function Create:TServer(port:Int=0,portrange:Int=40)
Local addr:Byte Ptr,server:TServer=New TServer

server.ip=DottedIPToInt(ENET_HOST_ANY)
If portrange<=1
server.port = port
addr=enet_address_create(server.ip,server.port)
server.enethost=enet_host_create(addr,32,0,0,0)
enet_address_destroy(addr)
Else
If port=0 port=7777
For Local n:Int=port To port+portrange-1
addr=enet_address_create(ENET_HOST_ANY,n)
server.enethost=enet_host_create(addr,64,0,0,0)
enet_address_destroy(addr)
If server.enethost
server.port=n
Exit
EndIf
Next
EndIf
If Not server.enethost Return Null
Return server
EndFunction

Method Publish:Int(servername:String="",gamename:String="",url:String="http::www.leadwerks.com/gameservers/gameservers.php")
Local stream:TStream
gamename=EncodeURL(gamename)
servername=EncodeURL(servername)
Self.url=url
stream=OpenStream(url+"?action=addserver&gamename="+gamename+"&servername="+port+"|"+servername)
If Not stream Return False
Self.url=url
While Not stream.Eof()
Local s:String=stream.ReadLine()
If s.Trim()
stream.close()
Return False
EndIf
Wend
stream.close()
lastrefreshtime=MilliSecs()
Return True
EndMethod

Method Remove:Int()
Local stream:TStream
stream=OpenStream(url+"?action=removeserver")
If stream
stream.close()
Return True
Else
Return False
EndIf
EndMethod

Function Query:String[](gamename:String="",url:String="")
Local stream:TStream,s:String,sarr:String[],n:Int

If url="" url="http::www.leadwerks.com/gameservers/gameservers.php"
stream=OpenStream(url+"?action=listservers&gamename="+gamename)
If Not stream Return Null
While Not stream.Eof()
s=ReadLine(stream)
If s.Trim().length>0
sarr=sarr[..sarr.length+1]
sarr[sarr.length-1]=s.Trim()
Local sa:String[]=sarr[sarr.length-1].split("|")
Local name:String=sa[0]
sarr[sarr.length-1]=HostIp(name)+"|"+sarr[sarr.length-1]
EndIf
Wend
stream.close()
Return sarr
EndFunction

Method Refresh:Int()
Local stream:TStream
stream=OpenStream(url+"?action=refreshserver")
If Not stream Return False
stream.close()
Return True
EndMethod

Method FindClientByName:TClient(name:String)
Return TClient(clientmap.valueforkey(name))
EndMethod

Method FindClientByPeer:TClient(peer:Byte Ptr)
Local client:TClient
For client=EachIn clients
If client.enetpeer=peer Return client
Next
EndMethod

Method FindClient:TClient(ip:Int,port:Int)
Local client:TClient
For client=EachIn clients
If client.ip=ip And client.port=port Return client
Next
EndMethod

Method Update()
Rem
If url
Local time:Int=MilliSecs()
If time-lastrefreshtime>serverupdatefrequency
lastrefreshtime=time
Refresh()
EndIf
EndIf
EndRem
Super.Update()
EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr)
Local client:TClient=FindClient(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
Select id

Case NETWORK_PINGRESPONSE
client.latency=MilliSecs()-packet.ReadInt()

Case NETWORK_LEAVEGAMEREQUEST
If client
Send(client,NETWORK_LEAVEGAMERESPONSE,Null,SEND_RELIABLE)
Local relay:TPacket=New TPacket
relay.WriteLine(client.name)
Broadcast(NETWORK_PLAYERLEFT,relay,SEND_RELIABLE)
clients.remove(client)
If clientmap.valueforkey(client.name)=client clientmap.remove(client.name)
EndIf

Case NETWORK_JOINREQUEST
If client
Disconnect(client,1)
Local relay:TPacket=New TPacket
relay.WriteLine(client.name)
Broadcast(NETWORK_PLAYERLEFT,relay,SEND_RELIABLE)
EndIf
client=TClient.Find(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
client.enetpeer=enetpeer

If IPBanned(client.ip)
Disconnect(client,0)
Return
Else
client.name=packet.ReadLine()
If Not FindClientByName(client.name)
clientmap.insert(client.name,client)
clients.AddLast(client)
Local responsepacket:TPacket=New TPacket
responsepacket.WriteByte(1)
responsepacket.WriteByte(clients.count()-1)
Local peer:TClient
For peer=EachIn clients
If peer<>client responsepacket.WriteLine(peer.name)
Next
Send(client,NETWORK_JOINRESPONSE,responsepacket,SEND_RELIABLE)
id=NETWORK_PLAYERJOINED

'Tell everyone he joined
responsepacket=New TPacket
responsepacket.WriteLine(client.name)
Broadcast(NETWORK_PLAYERJOINED,responsepacket,SEND_RELIABLE)

Else
packet=New TPacket
packet.WriteByte(0)'no, you can't joint
packet.WriteByte(1)'reason: name already taken
Send(client,NETWORK_JOINRESPONSE,packet,SEND_RELIABLE)
'Disconnect(client,0)
Return
EndIf
EndIf

Case NETWORK_CHANGENAMEREQUEST
Local name:String=packet.ReadLine()
packet=New TPacket
If client
Local oldname:String=client.name
If FindClientByName(name)<>Null And client.name<>name
packet.WriteByte(0)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
Return
Else
packet.WriteByte(1)
packet.WriteLine(name)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
clientmap.remove(client.name)
client.name=name
clientmap.insert(name,client)
If oldname<>name
packet=New TPacket
packet.WriteLine(oldname)
packet.WriteLine(name)
Broadcast(NETWORK_PLAYERCHANGEDNAME,packet,SEND_RELIABLE)
EndIf
EndIf
Else
If FindClientByName(name)=Null
packet.WriteByte(1)
packet.WriteLine(name)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
Return
EndIf
EndIf
Return

Case NETWORK_CONNECT
Return

Case NETWORK_DISCONNECT
If client
clients.remove(client)
If clientmap.valueforkey(client.name)=client clientmap.remove(client.name)
If Not client.enethost client.free()
EndIf

Case NETWORK_PINGREQUEST
If Not client
client=New TClient
client.enetpeer=enetpeer
EndIf
Send(client,NETWORK_PINGRESPONSE,packet)
Return

Case NETWORK_CHAT
Local relay:TPacket=New TPacket
Local count:Int=packet.ReadByte()
relay.WriteLine(client.name)
relay.WriteLine(packet.ReadLine())
If count=0
Broadcast(NETWORK_CHAT,relay,SEND_RELIABLE)
Else
For Local n:Int=1 To count
client=FindClientByName(packet.ReadLine())
If client
Send(client,NETWORK_CHAT,relay,SEND_RELIABLE)
EndIf
Next
EndIf
Return

EndSelect

If callback
If packet packet.seek(0)
callback(Self,client,id,packet)
EndIf
EndMethod

Method Send:Int(client:TClient,id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local enetpacket:Byte Ptr
Local result:Int

If Not client.enetpeer RuntimeError "Can't send to local client."

Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
result=(enet_peer_send(client.enetpeer,channel,enetpacket)=0)
Return result
EndMethod

Method Broadcast:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local result:Int=1
For Local client:TClient=EachIn clients
If Not Send(client,id,packet,flags,channel) result=0
Next
Return result
Rem
Local enetpacket:Byte Ptr
Local result:Int

Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
enet_host_broadcast(Self.enethost,channel,enetpacket)
EndRem
EndMethod

Method Disconnect(client:TClient,force:Int=False)
If client.enetpeer
If force
'enet_peer_reset(client.enetpeer)
Else
enet_peer_disconnect(client.enetpeer,0)
EndIf
clients.remove(client)
If Not client.enethost
client.link.remove()
EndIf
If clientmap.valueforkey(client.name)=client
clientmap.remove(client.name)
EndIf
client.enetpeer=Null
EndIf
EndMethod

Method BanIP(ip:Int)
bannedips=bannedips[..bannedips.length+1]
bannedips[bannedips.length-1]=ip
EndMethod

Method IPBanned:Int(ip:Int)
For Local n:Int=0 To bannedips.length-1
If ip=bannedips[n] Return True
Next
Return False
EndMethod

Method Kick(client:TClient)
BanIP(client.ip)
Disconnect(client)
EndMethod

EndType

Type TClient Extends TNetworkNode

Const maxplayers:Int=64

Global list:TList=New TList

Field name:String
Field link:TLink
Field server:TServer
Field connected:Int
Field joined:Int=0
Field callback(client:TClient,id:Int,packet:TPacket)
Field userdata:Object
Field channels:Int=16
Field latency:Int

Method Free()
If link
link.remove()
link=Null
EndIf
Super.Free()
EndMethod

Function Find:TClient(ip:Int,port:Int)
Local client:TClient
For client=EachIn list
If client.ip=ip And client.port=port Return client
Next
client=New TClient
client.ip=ip
client.port=port
client.link=list.addlast(client)
Return client
EndFunction

Function Create:TClient(port:Int=0,portrange:Int=40)
Const maxpeers:Int=32
Local client:TClient=New TClient
Local addr:Byte Ptr

If portrange<=1
client.ip=DottedIPToInt(ENET_HOST_ANY)
client.port=port
addr=enet_address_create(client.ip,client.port)
client.enethost=enet_host_create(addr,maxpeers,0,0,0)
enet_address_destroy(addr)
If Not client.enethost Return Null
client.link=list.addlast(client)
Else
If port=0 port=7776
For Local n:Int=port To port+portrange-1
addr=enet_address_create(ENET_HOST_ANY,n)
client.enethost=enet_host_create(addr,maxpeers,0,0,0)
enet_address_destroy(addr)
If client.enethost
client.port=port
Exit
EndIf
Next
EndIf

Return client
EndFunction

Method Disconnect(force:Int=False)
Const disconnecttimeout:Int=10000

If Not Self.enethost RuntimeError "Can't update a remote server."
If server
If force
'enet_peer_reset(server.enetpeer)
Else
Send(NETWORK_LEAVEGAMEREQUEST,Null,SEND_RELIABLE)
Local ev:ENetEvent=New ENetEvent
Local start:Int=MilliSecs()
Repeat
If MilliSecs()-start>disconnecttimeout Exit
If enet_host_service(Self.enethost,ev,100)
Select ev.event
Case ENET_EVENT_TYPE_RECEIVE
If ev.packet
Local id:Int,packet:TPacket
Local size:Int=bmx_enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,bmx_enet_packet_data(ev.packet),1)
If size>1
packet=New TPacket
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),bmx_enet_packet_data(ev.packet)+1,size-1)
EndIf
Select id
Case NETWORK_LEAVEGAMERESPONSE
Exit
EndSelect
EndIf
EndSelect
Else
Exit
EndIf
Forever
enet_peer_disconnect(server.enetpeer,0)
EndIf
server=Null
EndIf
joined=False
EndMethod

Method SetName(name:String)
If name.length>16 name=name[..16]
If server
Local packet:TPacket=New TPacket
packet.WriteLine(name)
Send(NETWORK_CHANGENAMEREQUEST,packet,SEND_RELIABLE)
Else
Self.name=name
EndIf
EndMethod

Method Connect:Int(ip:Int,port:Int)
Local addr:Byte Ptr

If ip=0 ip=2130706433
If Not Self.enethost RuntimeError "Remote client cannot connect to server."
If server Disconnect()
server=New TServer
server.ip=ip
server.port=port
addr=enet_address_create(server.ip,server.port)
server.enetpeer=enet_host_connect(enethost,addr,channels,0)
enet_address_destroy(addr)
If server.enetpeer=Null
server=Null
Return 0
EndIf

Rem
Local _callback:Byte Ptr=Null
_callback=callback
Local start:Int=MilliSecs()
Repeat
Update()
If MilliSecs()-start>10000 Or joined=1 Exit
Forever
callback=_callback
EndRem

Return 1
EndMethod

Method Send:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local enetpacket:Byte Ptr
Local result:Int

If Not connected Return 0

If Not server RuntimeError "Client is not connected."
Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
result=(enet_peer_send(server.enetpeer,channel,enetpacket)=0)
Return result
EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr)
Select id
Case NETWORK_CONNECT
Self.connected=True
packet=New TPacket
packet.WriteLine(name)
Send(NETWORK_JOINREQUEST,packet)

'Self.connected=True
'Self.Join()

Case NETWORK_JOINRESPONSE
joined=packet.ReadByte()
If joined=0 Disconnect()
Case NETWORK_CHANGENAMERESPONSE
If packet.ReadByte()=1
name=packet.ReadLine()
EndIf
Case NETWORK_PINGREQUEST
Send(NETWORK_PINGRESPONSE,packet)
Return
Case NETWORK_PINGRESPONSE
latency=MilliSecs()-packet.ReadInt()
packet.WriteInt(enet_peer_ip(enetpeer))
packet.WriteInt(enet_peer_port(enetpeer))
If enetpeer=pingpeer
enet_peer_disconnect(pingpeer,0)
pingpeer=Null
EndIf
EndSelect
If callback
If packet packet.seek(0)
callback(Self,id,packet)
EndIf
EndMethod

Method Join()
If Not joined
Local packet:TPacket=New TPacket
packet.WriteLine(name)
Send(NETWORK_JOINREQUEST,packet)
EndIf
EndMethod

Function ConvertEvent:Int(ev:EnetEvent,packet:TPacket)
Select ev.event
Case ENET_EVENT_TYPE_CONNECT
Return NETWORK_CONNECT
Case ENET_EVENT_TYPE_DISCONNECT
Return NETWORK_DISCONNECT
Case ENET_EVENT_TYPE_RECEIVE
If ev.packet
Local id:Int
Local size:Int=bmx_enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,bmx_enet_packet_data(ev.packet),1)
If size>1
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),bmx_enet_packet_data(ev.packet)+1,size-1)
EndIf
enet_packet_destroy(ev.packet)
Return id
EndIf
EndSelect
EndFunction

Field pingpeer:Byte Ptr

Method Ping:Int(ip:Int=0,port:Int=0)
Const disconnecttimeout:Int=1000
Local packet:TPacket=New TPacket,addr:Byte Ptr,enetpacket:Byte Ptr,ev:EnetEvent,result:Int,id:Int,start:Int

If server
If server.ip=ip And server.port=port ip=0
EndIf

If Not enethost RuntimeError("Can't ping remote client.")
If ip
If pingpeer
enet_peer_disconnect(pingpeer,0)
pingpeer=Null
EndIf

ev=New EnetEvent
addr=enet_address_create(ip,port)
pingpeer=enet_host_connect(enethost,addr,1,0)
enet_address_destroy(addr)
If pingpeer
start=MilliSecs()
Repeat
If MilliSecs()-start>disconnecttimeout
enet_peer_disconnect(pingpeer,0)
pingpeer=Null
'Print "Connect timeout"
Return 0
EndIf
If enet_host_service(enethost,ev,0)
id=ConvertEvent(ev,packet)
Select id
Case NETWORK_CONNECT
Exit
EndSelect
EndIf
Forever
Local data:Byte[5]
data[0]=NETWORK_PINGREQUEST
start=MilliSecs()
MemCopy(Varptr data[1],Varptr start,4)
enetpacket=enet_packet_create(data,data.length,0)
If enet_peer_send(pingpeer,0,enetpacket)=0
Return 1
Else
enet_peer_disconnect(pingpeer,0)
pingpeer=Null
'Print "can't send packet"
Return 0
EndIf
Else
'Print "Cant connect to peer at "+ip+", "+port
Return 0
EndIf
Else
packet:TPacket=New TPacket
packet.WriteInt(MilliSecs())
Return Send(NETWORK_PINGREQUEST,packet)
EndIf
EndMethod

Method Say:Int(text:String,recipients:String[]=Null)
Local packet:TPacket=New TPacket
If recipients
packet.WriteByte(recipients.length)
packet.WriteLine(text)
For Local n:Int=0 To recipients.length-1
packet.WriteLine(recipients[n])
Next
Else
packet.WriteByte(0)
packet.WriteLine(text)
EndIf
Return Send(NETWORK_CHAT,packet)
EndMethod

EndType


Type TPacket Extends TBankStream

Method New()
_bank=New TBank
EndMethod

EndType


Function CreatePacket:TPacket()
Local packet:TPacket=New TPacket
Return packet
EndFunction


This simple program fails with Segmentation fault (core dumped) on Linux (Ubuntu 20.04, x64).

SuperStrict

Import pub.enet

Include "network.bmx"

Global port:Int = 6138

Global server:TServer = TServer.Create(port)
If server
Print "started server"
While True
server.Update()
Wend
EndIf


Does anyone have any ideas?

markcwm

I made a few changes thanks to the compiler and it runs in Ubuntu, mostly had overload error where there was an extra parameter, also functions with prefix bmx_ were not in pub.enet.
Code (Blitzmax) Select
Private

Const ENET_PACKET_FLAG_UNSEQUENCED:Int=2
Const SERVERUPDATEFREQUENCY:Int=4*1000*60

Function enet_host_port:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[1]
Local port:Int=(Short Ptr peer)[4]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return port
EndFunction

Function enet_host_ip:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[1]
Local port:Int=(Short Ptr peer)[4]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return ip
EndFunction

Function enet_peer_port:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[3]
Local port:Int=(Short Ptr peer)[8]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return port
EndFunction

Function enet_peer_ip:Int( peer:Byte Ptr  )
Local ip:Int=(Int Ptr peer)[3]
Local port:Int=(Short Ptr peer)[8]
?LittleEndian
ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24)
?
Return ip
EndFunction

Public

Type TNetworkNode

Field port:Int
Field ip:Int
Field enethost:Byte Ptr
Field enetpeer:Byte Ptr

Method Delete()
Free()
EndMethod

Method Free()
If enethost
enet_host_destroy(enethost)
enethost=Null
EndIf
EndMethod

Method Update()

Local ip:Int,port:Int,client:TClient,ev:ENetEvent=New ENetEvent,id:Byte,packet:TPacket

If Not Self.enethost RuntimeError "Can't update a remote server."
Repeat
If enet_host_service(Self.enethost,ev,0)

Select ev.event

Case ENET_EVENT_TYPE_CONNECT
id=NETWORK_CONNECT

Case ENET_EVENT_TYPE_DISCONNECT
id=NETWORK_DISCONNECT

Case ENET_EVENT_TYPE_RECEIVE
Local size:Int=enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,enet_packet_data(ev.packet),1)
If size>1
packet=New TPacket
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),enet_packet_data(ev.packet)+1,size-1)
EndIf

Default
Continue

EndSelect

EvaluateEvent(id,packet,ev.peer)

Else
Exit
EndIf

If enethost=Null Exit

Forever

EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Abstract

EndType

'Public


Const NETWORK_CONNECT:Int=1
Const NETWORK_DISCONNECT:Int=2
Const NETWORK_PINGREQUEST:Int=3
Const NETWORK_PINGRESPONSE:Int=4
Const NETWORK_JOINREQUEST:Int=5
Const NETWORK_JOINRESPONSE:Int=6
Const NETWORK_CHAT:Int=7
Const NETWORK_LEAVEGAME:Int=8
Const NETWORK_CHANGENAMEREQUEST:Int=9
Const NETWORK_CHANGENAMERESPONSE:Int=10
Const NETWORK_PLAYERJOINED:Int=11
Const NETWORK_PLAYERLEFT:Int=12
Const NETWORK_LEAVEGAMEREQUEST:Int=13
Const NETWORK_LEAVEGAMERESPONSE:Int=14
Const NETWORK_PLAYERCHANGEDNAME:Int=15

Const SEND_RELIABLE:Int=ENET_PACKET_FLAG_RELIABLE
Const SEND_UNSEQUENCED:Int=ENET_PACKET_FLAG_UNSEQUENCED

Type TServer Extends TNetworkNode

Field clients:TList=New TList
Field callback(server:TServer,client:TClient,id:Int,packet:TPacket)
Field bannedips:Int[]
Field clientmap:TMap=New TMap
Field url:String
Field lastrefreshtime:Int

Method Free()
If url
Remove()
url=""
EndIf
Super.Free()
EndMethod

Function EncodeURL:String(t:String)
Local newString:String
Local i:Int,c:String,asciival:Int,hexstr:String,newhexstr:String
For i = 0 To Len(t)
c:String = t[i..i+1]
asciival = Asc(c)
If asciival > 32 And asciival < 123
' handle replacing the special set of chars
c = Replace(c,"'","%27")
c = Replace(c,"%","%25")
c = Replace(c,"<","%3C")
c = Replace(c,">","%3E")
c = Replace(c,"\","%5C")
c = Replace(c,"^","%5E")
c = Replace(c,"[","%5B")
c = Replace(c,"]","%5D")
c = Replace(c,"+","%2B")
c = Replace(c,"$","%24")
c = Replace(c,",","%2C")
c = Replace(c,"@","%40")
c = Replace(c,":","%3A")
c = Replace(c,";","%3B")
c = Replace(c,"/","%2F")
c = Replace(c,"!","%21")
c = Replace(c,"#","%23")
c = Replace(c,"?","%3F")
c = Replace(c,"=","%3D")
c = Replace(c,"&","%26")
newString:+c
Else
hexstr$ = Hex(asciival)
newhexstr$ = "%" + hexstr[Len(hexstr)-2..Len(hexstr)]
newstring:+newhexstr
EndIf
Next

newstring = newstring[..Len(newstring)-3]
Return newstring
EndFunction

Function Create:TServer(port:Int=0,portrange:Int=40)
Local addr:Byte Ptr,server:TServer=New TServer

server.ip=DottedIPToInt(ENET_HOST_ANY)
If portrange<=1
server.port = port
addr=enet_address_create(server.ip,server.port)
server.enethost=enet_host_create(addr,32,0,0)
enet_address_destroy(addr)
Else
If port=0 port=7777
For Local n:Int=port To port+portrange-1
addr=enet_address_create(ENET_HOST_ANY,n)
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 Return Null
Return server
EndFunction

Method Publish:Int(servername:String="",gamename:String="",url:String="http::www.leadwerks.com/gameservers/gameservers.php")
Local stream:TStream
gamename=EncodeURL(gamename)
servername=EncodeURL(servername)
Self.url=url
stream=OpenStream(url+"?action=addserver&gamename="+gamename+"&servername="+port+"|"+servername)
If Not stream Return False
Self.url=url
While Not stream.Eof()
Local s:String=stream.ReadLine()
If s.Trim()
stream.close()
Return False
EndIf
Wend
stream.close()
lastrefreshtime=MilliSecs()
Return True
EndMethod

Method Remove:Int()
Local stream:TStream
stream=OpenStream(url+"?action=removeserver")
If stream
stream.close()
Return True
Else
Return False
EndIf
EndMethod

Function Query:String[](gamename:String="",url:String="")
Local stream:TStream,s:String,sarr:String[],n:Int

If url="" url="http::www.leadwerks.com/gameservers/gameservers.php"
stream=OpenStream(url+"?action=listservers&gamename="+gamename)
If Not stream Return Null
While Not stream.Eof()
s=ReadLine(stream)
If s.Trim().length>0
sarr=sarr[..sarr.length+1]
sarr[sarr.length-1]=s.Trim()
Local sa:String[]=sarr[sarr.length-1].split("|")
Local name:String=sa[0]
sarr[sarr.length-1]=HostIp(name)+"|"+sarr[sarr.length-1]
EndIf
Wend
stream.close()
Return sarr
EndFunction

Method Refresh:Int()
Local stream:TStream
stream=OpenStream(url+"?action=refreshserver")
If Not stream Return False
stream.close()
Return True
EndMethod

Method FindClientByName:TClient(name:String)
Return TClient(clientmap.valueforkey(name))
EndMethod

Method FindClientByPeer:TClient(peer:Byte Ptr)
Local client:TClient
For client=EachIn clients
If client.enetpeer=peer Return client
Next
EndMethod

Method FindClient:TClient(ip:Int,port:Int)
Local client:TClient
For client=EachIn clients
If client.ip=ip And client.port=port Return client
Next
EndMethod

Method Update()
Rem
If url
Local time:Int=MilliSecs()
If time-lastrefreshtime>serverupdatefrequency
lastrefreshtime=time
Refresh()
EndIf
EndIf
EndRem
Super.Update()
EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr)
Local client:TClient=FindClient(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
Select id

Case NETWORK_PINGRESPONSE
client.latency=MilliSecs()-packet.ReadInt()

Case NETWORK_LEAVEGAMEREQUEST
If client
Send(client,NETWORK_LEAVEGAMERESPONSE,Null,SEND_RELIABLE)
Local relay:TPacket=New TPacket
relay.WriteLine(client.name)
Broadcast(NETWORK_PLAYERLEFT,relay,SEND_RELIABLE)
clients.remove(client)
If clientmap.valueforkey(client.name)=client clientmap.remove(client.name)
EndIf

Case NETWORK_JOINREQUEST
If client
Disconnect(client,1)
Local relay:TPacket=New TPacket
relay.WriteLine(client.name)
Broadcast(NETWORK_PLAYERLEFT,relay,SEND_RELIABLE)
EndIf
client=TClient.Find(enet_peer_ip(enetpeer),enet_peer_port(enetpeer))
client.enetpeer=enetpeer

If IPBanned(client.ip)
Disconnect(client,0)
Return
Else
client.name=packet.ReadLine()
If Not FindClientByName(client.name)
clientmap.insert(client.name,client)
clients.AddLast(client)
Local responsepacket:TPacket=New TPacket
responsepacket.WriteByte(1)
responsepacket.WriteByte(clients.count()-1)
Local peer:TClient
For peer=EachIn clients
If peer<>client responsepacket.WriteLine(peer.name)
Next
Send(client,NETWORK_JOINRESPONSE,responsepacket,SEND_RELIABLE)
id=NETWORK_PLAYERJOINED

'Tell everyone he joined
responsepacket=New TPacket
responsepacket.WriteLine(client.name)
Broadcast(NETWORK_PLAYERJOINED,responsepacket,SEND_RELIABLE)

Else
packet=New TPacket
packet.WriteByte(0)'no, you can't joint
packet.WriteByte(1)'reason: name already taken
Send(client,NETWORK_JOINRESPONSE,packet,SEND_RELIABLE)
'Disconnect(client,0)
Return
EndIf
EndIf

Case NETWORK_CHANGENAMEREQUEST
Local name:String=packet.ReadLine()
packet=New TPacket
If client
Local oldname:String=client.name
If FindClientByName(name)<>Null And client.name<>name
packet.WriteByte(0)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
Return
Else
packet.WriteByte(1)
packet.WriteLine(name)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
clientmap.remove(client.name)
client.name=name
clientmap.insert(name,client)
If oldname<>name
packet=New TPacket
packet.WriteLine(oldname)
packet.WriteLine(name)
Broadcast(NETWORK_PLAYERCHANGEDNAME,packet,SEND_RELIABLE)
EndIf
EndIf
Else
If FindClientByName(name)=Null
packet.WriteByte(1)
packet.WriteLine(name)
send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE)
Return
EndIf
EndIf
Return

Case NETWORK_CONNECT
Return

Case NETWORK_DISCONNECT
If client
clients.remove(client)
If clientmap.valueforkey(client.name)=client clientmap.remove(client.name)
If Not client.enethost client.free()
EndIf

Case NETWORK_PINGREQUEST
If Not client
client=New TClient
client.enetpeer=enetpeer
EndIf
Send(client,NETWORK_PINGRESPONSE,packet)
Return

Case NETWORK_CHAT
Local relay:TPacket=New TPacket
Local count:Int=packet.ReadByte()
relay.WriteLine(client.name)
relay.WriteLine(packet.ReadLine())
If count=0
Broadcast(NETWORK_CHAT,relay,SEND_RELIABLE)
Else
For Local n:Int=1 To count
client=FindClientByName(packet.ReadLine())
If client
Send(client,NETWORK_CHAT,relay,SEND_RELIABLE)
EndIf
Next
EndIf
Return

EndSelect

If callback
If packet packet.seek(0)
callback(Self,client,id,packet)
EndIf
EndMethod

Method Send:Int(client:TClient,id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local enetpacket:Byte Ptr
Local result:Int

If Not client.enetpeer RuntimeError "Can't send to local client."

Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
result=(enet_peer_send(client.enetpeer,channel,enetpacket)=0)
Return result
EndMethod

Method Broadcast:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local result:Int=1
For Local client:TClient=EachIn clients
If Not Send(client,id,packet,flags,channel) result=0
Next
Return result
Rem
Local enetpacket:Byte Ptr
Local result:Int

Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
enet_host_broadcast(Self.enethost,channel,enetpacket)
EndRem
EndMethod

Method Disconnect(client:TClient,force:Int=False)
If client.enetpeer
If force
'enet_peer_reset(client.enetpeer)
Else
enet_peer_disconnect(client.enetpeer)
EndIf
clients.remove(client)
If Not client.enethost
client.link.remove()
EndIf
If clientmap.valueforkey(client.name)=client
clientmap.remove(client.name)
EndIf
client.enetpeer=Null
EndIf
EndMethod

Method BanIP(ip:Int)
bannedips=bannedips[..bannedips.length+1]
bannedips[bannedips.length-1]=ip
EndMethod

Method IPBanned:Int(ip:Int)
For Local n:Int=0 To bannedips.length-1
If ip=bannedips[n] Return True
Next
Return False
EndMethod

Method Kick(client:TClient)
BanIP(client.ip)
Disconnect(client)
EndMethod

EndType

Type TClient Extends TNetworkNode

Const maxplayers:Int=64

Global list:TList=New TList

Field name:String
Field link:TLink
Field server:TServer
Field connected:Int
Field joined:Int=0
Field callback(client:TClient,id:Int,packet:TPacket)
Field userdata:Object
Field channels:Int=16
Field latency:Int

Method Free()
If link
link.remove()
link=Null
EndIf
Super.Free()
EndMethod

Function Find:TClient(ip:Int,port:Int)
Local client:TClient
For client=EachIn list
If client.ip=ip And client.port=port Return client
Next
client=New TClient
client.ip=ip
client.port=port
client.link=list.addlast(client)
Return client
EndFunction

Function Create:TClient(port:Int=0,portrange:Int=40)
Const maxpeers:Int=32
Local client:TClient=New TClient
Local addr:Byte Ptr

If portrange<=1
client.ip=DottedIPToInt(ENET_HOST_ANY)
client.port=port
addr=enet_address_create(client.ip,client.port)
client.enethost=enet_host_create(addr,maxpeers,0,0)
enet_address_destroy(addr)
If Not client.enethost Return Null
client.link=list.addlast(client)
Else
If port=0 port=7776
For Local n:Int=port To port+portrange-1
addr=enet_address_create(ENET_HOST_ANY,n)
client.enethost=enet_host_create(addr,maxpeers,0,0)
enet_address_destroy(addr)
If client.enethost
client.port=port
Exit
EndIf
Next
EndIf

Return client
EndFunction

Method Disconnect(force:Int=False)
Const disconnecttimeout:Int=10000

If Not Self.enethost RuntimeError "Can't update a remote server."
If server
If force
'enet_peer_reset(server.enetpeer)
Else
Send(NETWORK_LEAVEGAMEREQUEST,Null,SEND_RELIABLE)
Local ev:ENetEvent=New ENetEvent
Local start:Int=MilliSecs()
Repeat
If MilliSecs()-start>disconnecttimeout Exit
If enet_host_service(Self.enethost,ev,100)
Select ev.event
Case ENET_EVENT_TYPE_RECEIVE
If ev.packet
Local id:Int,packet:TPacket
Local size:Int=enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,enet_packet_data(ev.packet),1)
If size>1
packet=New TPacket
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),enet_packet_data(ev.packet)+1,size-1)
EndIf
Select id
Case NETWORK_LEAVEGAMERESPONSE
Exit
EndSelect
EndIf
EndSelect
Else
Exit
EndIf
Forever
enet_peer_disconnect(server.enetpeer)
EndIf
server=Null
EndIf
joined=False
EndMethod

Method SetName(name:String)
If name.length>16 name=name[..16]
If server
Local packet:TPacket=New TPacket
packet.WriteLine(name)
Send(NETWORK_CHANGENAMEREQUEST,packet,SEND_RELIABLE)
Else
Self.name=name
EndIf
EndMethod

Method Connect:Int(ip:Int,port:Int)
Local addr:Byte Ptr

If ip=0 ip=2130706433
If Not Self.enethost RuntimeError "Remote client cannot connect to server."
If server Disconnect()
server=New TServer
server.ip=ip
server.port=port
addr=enet_address_create(server.ip,server.port)
server.enetpeer=enet_host_connect(enethost,addr,channels)
enet_address_destroy(addr)
If server.enetpeer=Null
server=Null
Return 0
EndIf

Rem
Local _callback:Byte Ptr=Null
_callback=callback
Local start:Int=MilliSecs()
Repeat
Update()
If MilliSecs()-start>10000 Or joined=1 Exit
Forever
callback=_callback
EndRem

Return 1
EndMethod

Method Send:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0)
Local enetpacket:Byte Ptr
Local result:Int

If Not connected Return 0

If Not server RuntimeError "Client is not connected."
Local data:Byte[]
If packet
If packet._bank.size()=0 packet=Null
EndIf
If packet
data=New Byte[packet._bank.size()+1]
MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size())
Else
data=New Byte[1]
EndIf
data[0]=id
enetpacket=enet_packet_create(data,data.length,flags)
result=(enet_peer_send(server.enetpeer,channel,enetpacket)=0)
Return result
EndMethod

Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr)
Select id
Case NETWORK_CONNECT
Self.connected=True
packet=New TPacket
packet.WriteLine(name)
Send(NETWORK_JOINREQUEST,packet)

'Self.connected=True
'Self.Join()

Case NETWORK_JOINRESPONSE
joined=packet.ReadByte()
If joined=0 Disconnect()
Case NETWORK_CHANGENAMERESPONSE
If packet.ReadByte()=1
name=packet.ReadLine()
EndIf
Case NETWORK_PINGREQUEST
Send(NETWORK_PINGRESPONSE,packet)
Return
Case NETWORK_PINGRESPONSE
latency=MilliSecs()-packet.ReadInt()
packet.WriteInt(enet_peer_ip(enetpeer))
packet.WriteInt(enet_peer_port(enetpeer))
If enetpeer=pingpeer
enet_peer_disconnect(pingpeer)
pingpeer=Null
EndIf
EndSelect
If callback
If packet packet.seek(0)
callback(Self,id,packet)
EndIf
EndMethod

Method Join()
If Not joined
Local packet:TPacket=New TPacket
packet.WriteLine(name)
Send(NETWORK_JOINREQUEST,packet)
EndIf
EndMethod

Function ConvertEvent:Int(ev:EnetEvent,packet:TPacket)
Select ev.event
Case ENET_EVENT_TYPE_CONNECT
Return NETWORK_CONNECT
Case ENET_EVENT_TYPE_DISCONNECT
Return NETWORK_DISCONNECT
Case ENET_EVENT_TYPE_RECEIVE
If ev.packet
Local id:Int
Local size:Int=enet_packet_size(ev.packet)
Local data:Byte[size]
MemCopy(Varptr id,enet_packet_data(ev.packet),1)
If size>1
packet._bank.resize(size-1)
MemCopy(packet._bank.buf(),enet_packet_data(ev.packet)+1,size-1)
EndIf
enet_packet_destroy(ev.packet)
Return id
EndIf
EndSelect
EndFunction

Field pingpeer:Byte Ptr

Method Ping:Int(ip:Int=0,port:Int=0)
Const disconnecttimeout:Int=1000
Local packet:TPacket=New TPacket,addr:Byte Ptr,enetpacket:Byte Ptr,ev:EnetEvent,result:Int,id:Int,start:Int

If server
If server.ip=ip And server.port=port ip=0
EndIf

If Not enethost RuntimeError("Can't ping remote client.")
If ip
If pingpeer
enet_peer_disconnect(pingpeer)
pingpeer=Null
EndIf

ev=New EnetEvent
addr=enet_address_create(ip,port)
pingpeer=enet_host_connect(enethost,addr,1)
enet_address_destroy(addr)
If pingpeer
start=MilliSecs()
Repeat
If MilliSecs()-start>disconnecttimeout
enet_peer_disconnect(pingpeer)
pingpeer=Null
'Print "Connect timeout"
Return 0
EndIf
If enet_host_service(enethost,ev,0)
id=ConvertEvent(ev,packet)
Select id
Case NETWORK_CONNECT
Exit
EndSelect
EndIf
Forever
Local data:Byte[5]
data[0]=NETWORK_PINGREQUEST
start=MilliSecs()
MemCopy(Varptr data[1],Varptr start,4)
enetpacket=enet_packet_create(data,data.length,0)
If enet_peer_send(pingpeer,0,enetpacket)=0
Return 1
Else
enet_peer_disconnect(pingpeer)
pingpeer=Null
'Print "can't send packet"
Return 0
EndIf
Else
'Print "Cant connect to peer at "+ip+", "+port
Return 0
EndIf
Else
packet:TPacket=New TPacket
packet.WriteInt(MilliSecs())
Return Send(NETWORK_PINGREQUEST,packet)
EndIf
EndMethod

Method Say:Int(Text:String,recipients:String[]=Null)
Local packet:TPacket=New TPacket
If recipients
packet.WriteByte(recipients.length)
packet.WriteLine(Text)
For Local n:Int=0 To recipients.length-1
packet.WriteLine(recipients[n])
Next
Else
packet.WriteByte(0)
packet.WriteLine(Text)
EndIf
Return Send(NETWORK_CHAT,packet)
EndMethod

EndType


Type TPacket Extends TBankStream

Method New()
_bank=New TBank
EndMethod

EndType


Function CreatePacket:TPacket()
Local packet:TPacket=New TPacket
Return packet
EndFunction

wombats

I should have mentioned that I'm using the newest Linux released for NG that includes an updated version of pub.enet that requires the bmx_ prefix changes.

Derron

If you built the debug modules with "gdb" option enabled, you could run your application via
gdb -r myapp
(then "r" to run it ... and once it crashed, "bt" to see the backtrace of where it crashed and why)


bye
Ron

markcwm

#4
The gdb backtrace output for my NG v0.120 is:
(gdb) r
Starting program: /~/bmx-ng-0.120/projects/network_test.debug
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff3d6c700 (LWP 26857)]
started server

Thread 1 "network_test.de" received signal SIGSEGV, Segmentation fault.
bbObjectFree (o=0x7ffff7e4a1b0) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/blitz_object.c:71
71 clas->dtor( o );
(gdb) bt
#0  bbObjectFree (o=0x7ffff7e4a1b0) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/blitz_object.c:71
#1  0x0000000000734b0e in GC_invoke_finalizers () at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/finalize.c:1281
#2  0x0000000000734d1c in GC_notify_or_invoke_finalizers () at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/finalize.c:1360
#3  0x00000000007360c7 in GC_generic_malloc (lb=16, k=0) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/malloc.c:247
#4  0x000000000073683f in GC_malloc_kind_global (k=0, lb=<optimized out>) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/malloc.c:327
#5  GC_malloc_kind (k=0, lb=<optimized out>) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/malloc.c:333
#6  GC_malloc_atomic (lb=<optimized out>) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/bdwgc/malloc.c:340
#7  0x00000000007226fb in bbGCAllocObject (sz=<optimized out>, clas=clas@entry=0x9f9ca0 <pub_enet_ENetEvent>, flags=3)
    at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/blitz_gc.c:102
#8  0x0000000000718cd8 in bbObjectAtomicNew (clas=0x9f9ca0 <pub_enet_ENetEvent>) at /~/bmx-ng-0.120/mod/brl.mod/blitz.mod/blitz_object.c:47
#9  0x0000000000408ff0 in __m_network_test_TNetworkNode_Update (o=<optimized out>)
    at /~/bmx-ng-0.120/projects/.bmx/network_test.bmx.gui.debug.linux.x64.c:492
#10 0x0000000000408702 in __m_network_test_TServer_Update (o=<optimized out>)
    at /~/bmx-ng-0.120/projects/.bmx/network_test.bmx.gui.debug.linux.x64.c:1840
#11 0x0000000000417c34 in _bb_main () at /~/bmx-ng-0.120/projects/.bmx/network_test.bmx.gui.debug.linux.x64.c:6011
#12 0x0000000000712b43 in __bb_brl_appstub_appstub () at /~/bmx-ng-0.120/mod/brl.mod/appstub.mod/.bmx/appstub.bmx.debug.linux.x64.c:8
#13 0x00000000004082ff in main ()
(gdb)


I dropped pub.enet into NG v0.93 and got a segfault as well, this is my gdb backtrace:
(gdb) r
Starting program: /~/bmx-ng/network_test.debug
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff74e6700 (LWP 30241)]
started server

Thread 1 "network_test.de" received signal SIGSEGV, Segmentation fault.
0x000000000046d6e3 in bbObjectFree (o=0x9beb20)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/blitz_object.c:77
77 clas->dtor( o );
(gdb) bt
#0  0x000000000046d6e3 in bbObjectFree (o=0x9beb20)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/blitz_object.c:77
#1  0x0000000000485865 in GC_invoke_finalizers ()
    at /~/bmx-ng/mod/brl.mod/blitz.mod/bdwgc/finalize.c:1192
#2  0x0000000000485b03 in GC_notify_or_invoke_finalizers ()
    at /~/bmx-ng/mod/brl.mod/blitz.mod/bdwgc/finalize.c:1267
#3  0x0000000000486da7 in GC_generic_malloc (lb=16, k=0)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/bdwgc/malloc.c:177
#4  0x0000000000487268 in GC_malloc_atomic (lb=<optimized out>)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/bdwgc/malloc.c:249
#5  0x0000000000476398 in bbGCAllocObject (sz=<optimized out>,
    clas=clas@entry=0x6b32c0 <pub_enet_ENetEvent>, flags=3)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/blitz_gc.c:81
#6  0x000000000046d805 in bbObjectAtomicNew (
    clas=0x6b32c0 <pub_enet_ENetEvent>)
    at /~/bmx-ng/mod/brl.mod/blitz.mod/blitz_object.c:45
#7  0x00000000004042b2 in __m_network_test_TNetworkNode_Update (
    o=<optimized out>) at /~/bmx-ng/network.bmx:64
#8  0x0000000000403a33 in __m_network_test_TServer_Update (o=<optimized out>)
    at /~/bmx-ng/network.bmx:300
#9  0x0000000000412dfd in _bb_main () at /~/bmx-ng/network_test.bmx:18
#10 0x0000000000468c93 in __bb_brl_appstub_appstub ()
---Type <return> to continue, or q <return> to quit---
4.c:8
#11 0x000000000040365f in main (argc=1, argv=0x7fffffffdf88) at /~/bmx-ng/mod/brl.mod/appstub.mod/appstub.linux.c:18
(gdb)


I'm going to ask Derron to let us know what he thinks, cause I can't follow it.

col

Does the seg fault appear when building a 32bit version, (I'm thinking of pointer sizes with maybe a 64bit pointer stored in a 32bit integer).

Or

maybe a pointer manually deleted previous to the GC deleting an object and trying to delete again.
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

markcwm

#6
Yes, it also segfaults in 32bit. Here's the Win x86 backtrace for v0.120:
(gdb) r
Starting program: C:\bmx-ng-0.120\projects\network_test.debug.exe
[New Thread 1500.0x5bc]
warning: `C:\Windows\SYSTEM32\ntdll.dll': Shared library architecture i386:x86-6
4 is not compatible with target architecture i386.
warning: `C:\Windows\SYSTEM32\wow64.dll': Shared library architecture i386:x86-6
4 is not compatible with target architecture i386.
warning: `C:\Windows\SYSTEM32\wow64win.dll': Shared library architecture i386:x8
6-64 is not compatible with target architecture i386.
warning: `C:\Windows\SYSTEM32\wow64cpu.dll': Shared library architecture i386:x8
6-64 is not compatible with target architecture i386.
warning: Could not load shared library symbols for WOW64_IMAGE_SECTION.
Do you need "set solib-search-path" or "set sysroot"?
warning: Could not load shared library symbols for WOW64_IMAGE_SECTION.
Do you need "set solib-search-path" or "set sysroot"?
warning: Could not load shared library symbols for NOT_AN_IMAGE.
Do you need "set solib-search-path" or "set sysroot"?
warning: Could not load shared library symbols for NOT_AN_IMAGE.
Do you need "set solib-search-path" or "set sysroot"?
[New Thread 1500.0x9f0]

Program received signal SIGSEGV, Segmentation fault.
0x0000002b in ?? ()
(gdb) bt
#0  0x0000002b in ?? ()
(gdb)


The Win x64 backtrace yields more information:
(gdb) r
Starting program: C:\bmx-ng-0.120\projects\network_test.debug.exe
[New Thread 3008.0x584]
[New Thread 3008.0xbac]

Program received signal SIGSEGV, Segmentation fault.
0x00000000004369bd in ?? ()
(gdb) bt
#0  0x00000000004369bd in ?? ()
#1  0x00000000004022bc in ?? ()
#2  0x0000000000401837 in ?? ()
#3  0x000000000040fe12 in ?? ()
#4  0x000000000046ca7e in ?? ()
#5  0x000000000049c562 in ?? ()
#6  0x00000000004013c7 in ?? ()
#7  0x00000000004014cb in ?? ()
#8  0x00000000770159cd in KERNEL32!BaseThreadInitThunk ()
   from C:\Windows\system32\kernel32.dll
#9  0x000000007724a561 in ntdll!RtlUserThreadStart ()
   from C:\Windows\SYSTEM32\ntdll.dll
#10 0x0000000000000000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)

Derron

The error sounds - as col wrote - as if something cleaned up something which normally would be cleaned up by the GC ... so GC fails and you get the segfault.


#7  0x00000000004042b2 in __m_network_test_TNetworkNode_Update (
    o=<optimized out>) at /~/bmx-ng/network.bmx:64


Something there maybe


bye
Ron

wombats

Thanks for your replies and looking into it.

If I comment out If enet_host_service(Self.enethost,ev,0), it doesn't crash on macOS or give a Segmentation fault on Ubuntu, so I wonder what's going on with that function.

Derron

You could "debugstop" there ... and check out what "self.enethost" and "ev" contain - both are "Byte Ptr" and maybe they do no longer contain valid data behind that memory address.

Or maybe you send the wrong "ev" (there is "Type ENetEvent" - and surely some raw enet event in C).


bye
Ron

markcwm

#10
Setting timeout to 1 fixes it: enet_host_service(Self.enethost,ev,1).

Edit: actually no, timeout just delays the segfault.

Edit: The older pub.enet works with v0.120 but conflicts with brl.gnet.

wombats

Quote from: Derron on June 10, 2020, 08:56:36
You could "debugstop" there ... and check out what "self.enethost" and "ev" contain - both are "Byte Ptr" and maybe they do no longer contain valid data behind that memory address.

Or maybe you send the wrong "ev" (there is "Type ENetEvent" - and surely some raw enet event in C).


bye
Ron
"self.enethost" and "ev" both appear to be valid.

This code worked perfectly ten years ago, so I'm wondering if some change in BlitzMax NG is making that command crash.

Setting the timeout value to something higher like 500 or 1000 seems to let it run without crashing (but I don't know for how long), though I wonder what impact that would have on speed.

Derron

The enet library was updated a while ago - and maybe Brucey missed a bug sneaking in.


bye
Ron

markcwm

Well, while you're wait for Brucey to fix this bug with the new zpl-c enet, it seems you can copy over and use the brl.gnet and pub.enet modules from v0.93, although I only tested the code here by wombats.