Recent posts

#1
Thank you @Derron . This will solve @Baggey 's problems with performance.

You gave a lot of information! A little bit simplified this means, I can "store" 255 functions in an array and now call one of the 255 functions via it's array-index.
This would prevent @Baggey  from iterating through 255 Ifs or Cases.

Each Instruction-ID lead directly to the corresponding function without searching through a table:

(this is a runnable example)
SuperStrict
Global NextInstruction:Int=2

Global ActionTable()[4]
'  put the functions into the array:
ActionTable[1]=A
ActionTable[2]=B
ActionTable[3]=C

CallFunction NextInstruction

Function CallFunction(InstructionID:Int)
    ActionTable[InstructionID]()
End Function

Function A()
    Print "A"
End Function

Function B()
    Print "B"
End Function

Function C()
    Print "C"
End Function

And here is the same with parameters sent to the function:

SuperStrict
Global NextInstruction:Int=2

Global ActionTable(Value:Int)[4]
'  put the functions into the array:
ActionTable[1]=A
ActionTable[2]=B
ActionTable[3]=C

CallFunction NextInstruction, 4

Function CallFunction(InstructionID:Int, value:Int)
    ActionTable[InstructionID] value
End Function

Function A(Value:Int)
    Print "A" + Value
End Function

Function B(Value:Int)
    Print "B" + Value
End Function

Function C(Value:Int)
    Print "C" + Value
End Function


And here is the same with returning values:

SuperStrict
Global ActionTable:Int(Value:Int)[4]
'  put the functions into the array:
ActionTable[1] = Plus
ActionTable[2] = Minus
ActionTable[3] = Square

Print CallFunction(1,4)
Print CallFunction(2,4)
Print CallFunction(3,4)

Function CallFunction:Int(InstructionID:Int, Value:Int)
    Local f:Int(t:Int) = ActionTable[InstructionID]
    Return f(Value)
    'Return ActionTable[InstructionID](Value)
End Function

Function Plus:Int(Value:Int)
    Print "function PLUS 5+" + Value + " = " + (Value+5)
    Return (Value+5)
End Function

Function Minus:Int(Value:Int)
    Print "function MINUS 5-" + Value + " = " + (5-Value)
    Return (5-Value)
End Function

Function Square:Int(Value:Int)
    Print "function SQUARE " + Value + "^2 = " + (Value^2)
    Return (Value^2)
End Function

But why do I need two code lines in the CallFunction() and cannot be done with the one line?
#2
AppGameKit ( AGK ) / Re: AGK Studio as a last langu...
Last post by Derron - Yesterday at 13:31:44
If life is too short then you should consider not jumping across languages (except your hobby is to explore languages...) and instead focus on learning to make (read: "complete") a game.

You only jump from language to language, tell that you did something in the past ... but instead of improving your "game coding/asset quality" you blame the languages. Others complete their games in all the tools you tried - wonder how sophisticated yours must be to not run there.


Just to bring it in again: Give Godot (or unity or ...) a try - give it another try once you completed some simple game. There is a reason that these tools are used by small and bigger companies. They are not all "clickibunti"-users just capable of pushing their mice around. You can code along with these engines, you can achieve really interesting things. But as they offer more (mostly including what you desire from the "one man band"-engines) you will also have to maybe setup more stuff for their "world environments". In most cases whatever your "problem" is ... is not an issue there, as in these engines you do not even have to take care of it (they do it for you). Doing the "labor work" there, is for this reason less convenient (eg. bone movement compared to maybe Blitz3D). It is just not needed because normally such stuff is done by an animator, or yourself in a program suited for animation creation.

Anyways - your chances to get someone helping you / listening to your "issues" (bug reports, incompatibilities...) for Godot are way higher than for PB, B3D, BlitzMax, AGK, ... their communities are just bigger by many levels.


bye
Ron
#3
BlitzMax / BlitzMax NG / Re: Case statement sequence?
Last post by Derron - Yesterday at 13:16:28
You simply create an array of something.

If you want to store a kind of "config" together with the function itself you simply do
Type TFunctionHandler
   Method Run:Int()
   End Method
End Type

Global functionHandlers:TFunctionHandler[255] '255 handlers?

...

Type TFunctionHandlerXYZ extends TFunctionHandler
  Method New(param1:Int, param2:String)
     ...
  End Method


  Method Run:Int()
    if param2 is "hello" then return param1
    return 0
  End Method
End Type


functionHandlers[1 -1] = New TFunctionHandlerXYZ(100, "hello")


receivedCommand = somewhereSomehow...
if receivedCommand <= functionHandlers.length and receivedCommand > 0
  local handler:TFunctionHandler = functionHandlers[receivedCommand -1]
  if handler then handler.run()
endif


Alternatively - if you want to avoid the "type" overhead and have a unified "param"-style, then you can do

Global commandFunctions:Int(o:object)[255] '255 of them?
'or
'Global commandFunctions:Int(o:object)[] 'empty at initialization


Function CommandXYZFunction:Int(o:Object)
End Function

commandFunctions[1 -1] = CommandXYZFunction

...

params = new TMap 'or a list or a string, array, ... some object
if receivedCommand <= commandFunctions.length and receivedCommand > 0
  local f:int(o:object) = commandFunctions[receivedCommand -1]
  if f then f(params)
endif



And last but not least - how to store the function in type instance
Type TFunctionHandler
   Field myFunction:Int(params:object)

   Method Run:Int(params:object)
    if myFunction Then Return myFunction(params)
    Return 0
   End Method
End Type

Global functionHandlers:TFunctionHandler[255] '255 handlers?

...
Function CommandXYZFunction:Int(o:Object)
End Function

functionHandlers[1 -1] = New TFunctionHandler()
functionHandlers[1 -1].myFunction = CommandXYZFunction


params = new TMap 'or a list or a string, array, ... some object
receivedCommand = somewhereSomehow...
if receivedCommand <= functionHandlers.length and receivedCommand > 0
  local handler:TFunctionHandler = functionHandlers[receivedCommand -1]
  if handler then handler.run(params)
  'or directly... if you never override TFunctionHandler
  if handler and handler.myFunction Then handler.myFunction(params)
endif



bye
Ron
#4
AppGameKit ( AGK ) / Re: AGK Studio as a last langu...
Last post by Pfaber11 - Yesterday at 07:57:24
You may be right @Alien but I spent a year nearly with it and I find OGRE to be too old. But before I do anything else I will see if we have a decent game engine to use instead. I do like PB but the 3D I do not like as it is old. I really do not want to attempt to write my own Engine as life is too short.
#5
AppGameKit ( AGK ) / Re: AGK Studio as a last langu...
Last post by Naughty Alien - May 03, 2024, 04:28:57
..i think you havent explored PB in to depth, hence you have problems with it, as it 'doesnt provide' what you want, just a click away..i think you will not progress much, if you keep jumping between languages, unless thats what is your aim..PB as well as any other language is just fine, if you really dig in to it and learn how to use it well..

..as example, here is dude who made 3D engine..in MS Excel..so, there you go..

#6
BlitzMax / BlitzMax NG / Re: Networking - HTTPGet
Last post by _PJ_ - May 02, 2024, 13:04:17

Yep, I think I will need the secure http versions...

Thanks, Midimaster - I'll have a look over that LibCurl stuff. It seems to be exactly what I was looking for!
#7
AppGameKit ( AGK ) / Re: AGK Studio as a last langu...
Last post by Pfaber11 - May 02, 2024, 12:54:57
Hey there well I have found a language I would like to learn which is general purpose but I thought I would ask you what you thought of it , and it is "Julia" I'm not in a rush to learn it as I'm still using AGK Studio and I don't see that changing any time soon. In comparison to mojo by Modular I think Julia suits my needs better than mojo as I can use @label and @goto . I've decided to spend about an hour a day on it and see how I get on. I don't see myself giving up AGK Studio as it is such a pleasure 
to use and I am enjoying using it more as I progress as I'm getting things done faster. As far as it goes if AGK Studio was mainstream I think for me this would be it. I  said I was not going to learn another language after my almost year learning PureBasic had to come to an end. What I can build in AGK Studio is much better than PB as far as 3D goes . No proper smoothing in Height maps to make nice flowing hills, shadows not displaying properly,  the way the sun works I do not like. These things and the fact I have to use .mesh files . Anyway these things work well in AGK Studio and they are important things to consider. AGK is better for me and probably most people wanting to use 3D. 
#8
BlitzMax / BlitzMax NG / Re: Networking - HTTPGet
Last post by Midimaster - May 02, 2024, 11:58:32
I guess you need less code to download from the internet in BlitzMax

SuperStrict
Global PlainText:String = LoadText("http::www.midiox.com")
Print PlainText

This is a technical minimum, but works for contact with not https-Servers like "http://www...."

This is the same for streams:

Const Header:String = "http::"
Const Adress:String = "www.anywhere.com/anypage.html"

FetchPage(Header + Adress)

Function FetchPage(Adress:String)
    ' fetches a complete html-page in steps:
    Local Whole:String
    Print Adress
    Local Http:TStream=ReadStream(Adress)
    While Eof(Http)=False
        Whole = Whole + ReadLine(Http)
    Wend
    CloseStream Http
    Print Whole
End Function


For the meanwhile mostly used https protocol, you have to do more. You need the LibCurl Library. Have a look on my tutorial here:

https://www.syntaxbomb.com/worklogs/fetch-a-html-document-from-a-https-server-with-blitzmax-ng/msg347059214/#msg347059214

You need to read the step 2 and step 4

#9
Blitz2D, BlitzPlus, Blitz3D / Re: Blitz3D has been updated
Last post by _PJ_ - May 02, 2024, 11:52:48
Quote from: LineOf7s on May 01, 2024, 23:09:10Hope that helps.
Good news :)

Really helps- Thanks!
#10
BlitzMax / BlitzMax NG / Networking - HTTPGet
Last post by _PJ_ - May 02, 2024, 10:55:54
I have been really struggling with what I expected to be a straightforward problem :
Downloading of html from a given URL

I had been using the following (( some dependencies for constants and filepath handling etc. are not included )) in B3D / B+ but was unabble to convert to BMax because of how BMax uses the Sockets etc.

;These provide a set of control codes. Typically the entry would be akin to &#x22; or &#x26 etc.
Const HTM_CODE_LOWER_PREFIX$="&#x"
Const HTM_CODE_LOWER_SUFFIX$=";"

Const HTM_CARRIAGE_RETURN$="<br>"

Const HTM_STANDARDSYMBOLS_PREFIX$="<"
Const HTM_STANDARDSYMBOLS_SUFFIX$=">"

Const HTM_ENCODED_URL_CHARACTERS$="!£$^*()-=[]{}¬`;:'@#~|<>,?"

; FIXES CONTROL CODES USED BY WEBPAGE HTML
Function HTM_CleanControlCodesText$(sText$)
DebugLog("HTM (Clean HTM): Cleaning HTML Control Codes")

; Non-Standard symbolisations
Local IterFixes

Local SeekEnd=0
Local SeekStart=0
Local SeekWidth=0

Local MidString$=""

; Fix for typical html control codes with <> tags
SeekStart=Instr(sText,HTM_STANDARDSYMBOLS_PREFIX)
If (SeekStart)
While (SeekStart)
SeekEnd=Instr(sText,HTM_STANDARDSYMBOLS_SUFFIX,SeekStart)
SeekWidth=(SeekEnd-SeekStart)

If (SeekStart*SeekEnd)
MidString=Mid(sText,SeekStart,SeekWidth+1)
DebugLog("HTM (Clean HTM): Removed instances of code: "+Chr(34)+MidString+Chr(34))
sText=Replace(sText,MidString,"")
End If
SeekStart=Instr(sText,HTM_STANDARDSYMBOLS_PREFIX)
Wend
End If

; FIX FOR ATYPICAL CONTROL CODES FOR SYMBOLS WITH CONVERTED HEX NOTATIONS (i.e. &#xnn )
SeekStart=Instr(Lower(sText),HTM_CODE_LOWER_PREFIX)
If (SeekStart)
While (SeekStart)
SeekEnd=Instr(Lower(sText),HTM_CODE_LOWER_SUFFIX,SeekStart)
SeekWidth=(SeekEnd-SeekStart)
If (SeekStart*SeekEnd)
MidString=Mid(sText,SeekStart,SeekWidth+1)
DebugLog("HTM (Clean HTM): Removed instances of code: "+Chr(34)+MidString+Chr(34))
sText=Replace(sText,MidString,"")
End If
SeekStart=Instr(Lower(sText),HTM_CODE_LOWER_PREFIX)
Wend
End If

; Remove any remaining ascii control codes
DebugLog("HTM (Clean HTM): Removing ASCII code instances")
Local IterAsc
For IterAsc=0 To 31
sText=Replace(sText,Chr(IterAsc),"")
Next

Return sText$
End Function

;ENCODES CHARACTERS IN A STRING SUITABLE FOR URL ENCODING
Function HTM_EncodeURL$(sText$)

DebugLog("HTM (Fix url): Encoding "+Chr(34)+sText+Chr(34))

sText=Replace(Lower(Trim(sText)),"_","+")
sText=Replace(Lower(Trim(sText))," ","+")
sText=Replace(sText,"&","+")
sText=Replace(sText,STRING_PERCENT,"percent")
sText=Replace(sText,Chr(34),"")

Local Count
Local Char$
For Count=1 To Len(sText)
Char=(Mid(sText,Count,1))
Replace(sText,Char,HTM_URLEncode(Char))
Next

;Deal with multiple +'s
While Instr(sText,"++")
sText=(Replace(sText,"++","+"))
Wend

sText=Lower(Trim(sText))

DebugLog("HTM (Fix url): Result of encoding: "+Chr(34)+sText+Chr(34))

Return sText

End Function

;ENCODES NON-ALPHANUMERIC CHARACTERS WITH VALID URL HTML CODES
Function HTM_URLEncode(s_Char)
If (Not(GetIsAlphanumericCharacter(s_Char)))
If (Instr(HTM_ENCODED_URL_CHARACTERS,s_Char))
s_Char=STRING_PERCENT+Str(Asc(s_Char))
End If
End If
Return s_Char
End Function
;~IDEal Editor Parameters:
;~C#Blitz+

;________________________________________________________________________________________________________________

Const NET_PROTOCOL_HTTP$="http"
Const NET_PROTOCOL_HTTPS$="https"
Const NET_PROTOCOL_FTP$="ftp"
Const NET_WORLDWIDEWEB$="www"

Const NET_PROTOCOL_HANDLE_HTTP09$="HTTP/0.9"
Const NET_PROTOCOL_HANDLE_HTTP10$="HTTP/1.0"
Const NET_PROTOCOL_HANDLE_HTTP11$="HTTP/1.1"

Const NET_VALID_HEX$="123456789ABCDEF"

Const NET_USERAGENT$="EKD_Engine_Agent"
Const NET_BROWSERAGENT_IE11$="Mozilla/5.0 (Windows NT 6.3; Win64; x64; Trident/7.0; rv:11.0) like Gecko"
Const NET_BROWSERAGENT_IE8$="Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2)"
Const NET_BROWSERAGENT_IE6$="Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;)"

Const NET_PROTOCOL_SUFFIX$="://";cs_STRING_COLON+cs_STRING_WEB_SEPARATOR+cs_STRING_WEB_SEPARATOR

Const NET_DLE_FLAGS=7

Const NET_DLE_COM=1
Const NET_DLE_UK=2
Const NET_DLE_ORG=4

Const NET_DOMAIN_COM$="com"
Const NET_DOMAIN_UK$="co.uk"
Const NET_DOMAIN_ORG$="org"

Const NET_LOCAL_SEND_GET$="GET"
Const NET_LOCAL_SEND_POST$="POST"

Const NET_INVALIDFILECHARS$="=*:?<>|"

Const NET_SERVER_MESSAGE_REDIRECT$="location"
Const NET_SERVER_MESSAGE_LENGTH$="content-length"
Const NET_SERVER_MESSAGE_ENCODING$="transfer-encoding"
Const NET_SERVER_MESSAGE_CHUNKED$="chunked"

Const NET_PORT_DEFAULT_HTTP_PORT=80
Const NET_PORT_DEFAULT_HTTPS_PORT=443

Const NET_RESPONSE_OK=200
Const NET_RESPONSE_OK_CREATE=201
Const NET_RESPONSE_OK_ACCEPT=202
Const NET_RESPONSE_OK_PARTIAL=203
Const NET_RESPONSE_OK_NOREPLY=204

Const NET_RESPONSE_OK_MESSAGE=220

Const NET_RESPONSE_BAD_REQUEST=400
Const NET_RESPONSE_BAD_UNAUTHORISED=401
Const NET_RESPONSE_BAD_PAYMENT=402
Const NET_RESPONSE_BAD_FORBIDDEN=403
Const NET_RESPONSE_BAD_NOTFOUND=404

Const NET_RESPONSE_ERR_SERVER=500
Const NET_RESPONSE_ERR_NOTIMPLEMENTED=501
Const NET_RESPONSE_ERR_BANDWIDTH=502
Const NET_RESPONSE_ERR_TIMEOUT=503

Const NET_RESPONSE_REDIR_MOVED=301
Const NET_RESPONSE_REDIR_FOUND=302
Const NET_RESPONSE_REDIR_METHOD=303
Const NET_RESPONSE_REDIR_NOCHANGE=304

Const NET_RESPONSE_QUERY_PASS=331

Const NET_RESPONSE_ACTION_ERROR=0
Const NET_RESPONSE_ACTION_DOWNLOAD=1
Const NET_RESPONSE_ACTION_REDIRECT=2

Const NET_CHUNKSIZE=4096

Const NET_TIMEOUT_MILLISECS=5000

 ; WRITES BYTES FROM STREAM TO FILE
Function NET_DownloadFromStream(hst_Stream, DownloadPath$)
Local n4_ContentLength=-1
Local nb_Chunked = False

Local s_Line$

Local ns_Pos
Local s_Part1$
Local s_Part2$

;Recurse Bytes from stream...
Repeat

s_Line$ = Trim(NET_ReadStreamLine$(hst_Stream))

DebugLog("NET (DownloadFromStream): Server: "+s_Line)

If (s_Line$ = "")
;END OF STREAM!
DebugLog("NET (DownloadFromStream): Null data line from server")
Exit
End If

ns_Pos = Instr(s_Line,":")
s_Part1 = Trim(Left(s_Line, ns_Pos - 1))
s_Part2 = Trim(Right(s_Line, Len(s_Line) - ns_Pos))

Select (Lower(s_Part1$))
Case NET_SERVER_MESSAGE_LENGTH:
n4_ContentLength = Int(s_Part2)
Case NET_SERVER_MESSAGE_ENCODING:
If (Lower(s_Part2$) = Lower(NET_SERVER_MESSAGE_CHUNKED))
nb_Chunked = True
End If
End Select

Until (s_Line="")

If (Eof(hst_Stream)<0)
DebugLog("NET (DownloadFromStream): Error in processing stream. Aborting process")
NET_CloseTCPConnection(hst_Stream)
Return False
End If

Local hst_File=False

hst_File = WriteFile(DownloadPath$)

Local mb_Bank
Local n4_Available

If (Not(n4_ContentLength))
DebugLog("NET (DownloadFromStream): Data length not provided")

CloseFile hst_File
hst_File=False

NET_CloseTCPConnection(hst_Stream)

Return (FileType(DownloadPath)=1)

Else

DebugLog("NET (DownloadFromStream): Retreiving data form server...")

If (Not(nb_Chunked))

DebugLog("NET (DownloadFromStream): Data not served in chunked format")

mb_Bank = CreateBank(NET_CHUNKSIZE)
ns_Pos = 0

Repeat
n4_Available = (n4_ContentLength - ns_Pos)

If (n4_Available > NET_CHUNKSIZE)
ReadBytes mb_Bank, hst_Stream, 0, NET_CHUNKSIZE
WriteBytes mb_Bank, hst_File, 0, NET_CHUNKSIZE
ns_Pos = ns_Pos + NET_CHUNKSIZE

Else

ReadBytes mb_Bank, hst_Stream, 0, n4_Available
WriteBytes mb_Bank, hst_File, 0, n4_Available

ns_Pos=n4_Available
Exit
EndIf

Until ns_Pos=0

FreeBank mb_Bank

CloseFile hst_File
NET_CloseTCPConnection(hst_Stream)
hst_File=False

Return (FileType(DownloadPath)=1)
End If

If (nb_Chunked)
DebugLog("NET (DownloadFromStream): Data served in chunked format")

If (Not(hst_File))
hst_File =WriteFile(DownloadPath)
End If

mb_Bank = CreateBank(NET_CHUNKSIZE)
Local ns_IterBits

Repeat

s_Line$ = Trim(Upper(NET_ReadStreamLine$(hst_Stream)))

ns_Pos = 0

For ns_IterBits = 1 To Len(s_Line$)
ns_Pos = 16 * ns_Pos + Instr(NET_VALID_HEX,Mid$(s_Line, ns_IterBits, 1))
Next

If (Not(ns_Pos))
Exit
End If

If (BankSize(mb_Bank) < ns_Pos)
ResizeBank mb_Bank, ns_Pos
End If

ReadBytes mb_Bank, hst_Stream, 0, ns_Pos
WriteBytes mb_Bank, hst_File, 0, ns_Pos

ReadShort(hst_Stream)

Until ns_Pos=0

FreeBank mb_Bank

CloseFile hst_File
hst_File=False

NET_CloseTCPConnection(hst_Stream)
Return (FileType(DownloadPath)=1)
Else
DebugLog("NET (DownloadFromStream): Data served in invalid format")

NET_CloseTCPConnection(hst_Stream)

CloseFile hst_File
hst_File=False

Return False
EndIf
End If
End Function

;Function NET_ReadStreamBytes(hst_StreamHandle,hmb_BankHandle,n4_ByteCount=1,n4_Offset=0)
; If (Not(hst_StreamHandle))
; DebugLog("NET (ReadBytes: Stream Handle is null")
; Return
; End If
;
; WriteBytes(hmb_BankHandle,hst_StreamHandle,n4_Offset,n4_ByteCount)
;
;End Function

;RETURNS LINE STRING READ FROM STREAM
Function NET_ReadStreamLine$(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadLinet): Stream Handle is null")
Return ""
End If

Local s_Line$=ReadLine(hst_StreamHandle)
Return s_Line$
End Function

;RETURNS STRING READ FROM STREAM
Function NET_ReadStreamString$(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadString): Stream Handle is null")
Return ""
End If

Local s_String$=ReadString(hst_StreamHandle)

Return s_String

End Function

;RETURNS 4-BYTE INTEGER READ FROM STREAM
Function NET_ReadStreamInt%(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadInt): Stream Handle is null")
Return 0
End If

Local n4_Int$=ReadInt(hst_StreamHandle)

Return n4_Int

End Function

;RETURNS 2-BYTE INTEGER READ FROM STREAM
Function NET_ReadStreamShort(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadShort): Stream Handle is null")
Return 0
End If

Local ns_Short=ReadShort(hst_StreamHandle)

Return ns_Short

End Function

;RETURNS 1-BYTE INTEGER READ FROM STREAM
Function NET_ReadStreamByte(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadByte): Stream Handle is null")
Return 0
End If
Local m_Byte=ReadByte(hst_StreamHandle)

Return m_Byte
End Function

;RETURNS 4-BYTE FLOATING POINT VALUE READ FROM STREAM
Function NET_ReadStreamFloat#(hst_StreamHandle)
If (Not(hst_StreamHandle))
DebugLog("NET (ReadFloat): Stream Handle is null")
Return 0.0
End If

Local f_Float#=ReadFloat(hst_StreamHandle)

Return f_Float#

End Function

; SIMPLIFIES HTTP SERVER RESPONSE
Function NET_ResolveServerResponse(hst_StreamHandle)

If (Not(hst_StreamHandle))
DebugLog("NET (Resolve Response): Null stream handle, response resolved as bad")
Return NET_RESPONSE_BAD_REQUEST
End If

Local sLine$ = ReadLine(hst_StreamHandle)

DebugLog("NET (Resolve Response): Response "+Chr(34)+sLine+Chr(34))

If (sLine="")
DebugLog("NET (Resolve Response): No response message from server")
Return NET_RESPONSE_BAD_REQUEST
End If

Local ns_Pos1 = Instr(sLine$," ")
Local ns_Pos2 = Instr(sLine$," ", ns_Pos1 + 1)

If (Not(ns_Pos1*ns_Pos2))
DebugLog("NET (Resolve Response): Incorrect message format from server")
Return NET_RESPONSE_BAD_REQUEST
End If

Return Int(Mid(sLine$,ns_Pos1, ns_Pos2 - ns_Pos1))
End Function

; WHEN ENCOUNTERING 3XX RESPONSE FROM SERVER (CONTENT REDIRECTION), NEXT SERVER TRANSMISSION SHOULD BE REDIRECTION PATH
Function NET_LocateRedirectionPathFromStream$(hst_Stream)
Local s_Line$

Local Redir$
Local Pos

Local snt_NewPath$

Repeat
s_Line$ = Trim(ReadLine(hst_Stream))

DebugLog("NET (Redirect): Server: "+s_Line)

If (s_Line="")
Exit
End If

Pos = Instr(s_Line$,":")

If (Pos)
If (Lower(Trim(Left(s_Line$,Pos-1)))=Lower(NET_SERVER_MESSAGE_REDIRECT))
snt_NewPath = FixPath(Trim(Right(s_Line$, Len(s_Line$) - Pos)),False,True)
DebugLog("NET (Redirect): Obtained redirected path for content: "+snt_NewPath)
DebugLog("NET (Redirect): Closing current stream to commence new download from relocation.")
NET_CloseTCPConnection(hst_Stream)

Return snt_NewPath$
End If
End If

Until s_Line=""

DebugLog("NET (Redirect): Unable to obtain redirection path. Download aborted")
NET_CloseTCPConnection(hst_Stream)

Return ""
End Function

; RETURNS DOMAIN NAME AND DLE ONLY
Function NET_RetrieveDomain$(snt_Path$)
Local s_Sectors$=NET_TrimProtocolHead(snt_Path)
snt_Path$ = Left(s_Sectors,Instr(s_Sectors,"/")-1)

Return snt_Path
End Function

; OPENS TCP CONNECTION STREAM TO DOWNLOAD PATH AND RETURNS HANDLE
Function NET_CreateTCPConnection(snt_FullServerPath$)
snt_FullServerPath=FixPath(snt_FullServerPath,False,True)

Local s_Protocol$=NET_GetProtocolPrefix$(snt_FullServerPath$)
Local ns_Port=NET_GetProtocolPort(snt_FullServerPath)
Local snt_Domain$=NET_RetrieveDomain(snt_FullServerPath)
DebugLog("NET (Create TCP): Connection Polling "+snt_Domain+" via port "+Str(ns_Port))
;Open TCP stream
Local hst_tcp = OpenTCPStream(snt_Domain$, ns_Port )
DebugLog("NET (Create TCP): Connection 0x"+Str(hst_tcp)+" opened to "+DottedIP(TCPStreamIP(hst_tcp))+":"+Str(ns_Port))
Return hst_tcp
End Function

; CLOSES TCP STREAM CONNECTION
Function NET_CloseTCPConnection(hst_TCP=False)
If (hst_TCP)
DebugLog("NET (Close TCP): Stream 0x"+Str(hst_TCP)+" to "+DottedIP(TCPStreamIP(hst_TCP))+":"+Str(TCPStreamPort( hst_TCP ))+" closed")
CloseTCPStream(hst_TCP)
Else
DebugLog("NET (Close TCP): Attempted To close Null stream")
End If
End Function

;RETUIRNS A SIMPLIFIED VALUE DETERMINING APPROPRIATE ACTION ACCORDING TO RESPONSE VALUE
Function NET_GetActionByServerResponse(ns_ServerResponseCode=0)
Select (ns_ServerResponseCode)
Case NET_RESPONSE_OK, NET_RESPONSE_OK_ACCEPT, NET_RESPONSE_OK_CREATE, NET_RESPONSE_OK_MESSAGE, NET_RESPONSE_OK_NOREPLY, NET_RESPONSE_OK_PARTIAL:
DebugLog("NET (GetAction): Permission granted: Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_DOWNLOAD
Case NET_RESPONSE_REDIR_MOVED,NET_RESPONSE_REDIR_FOUND,NET_RESPONSE_REDIR_METHOD, NET_RESPONSE_REDIR_NOCHANGE:
DebugLog("NET (GetAction): Destination redirected: Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_REDIRECT
Case NET_RESPONSE_BAD_NOTFOUND:
DebugLog("NET (GetAction): Destination not found : "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_ERROR
Case NET_RESPONSE_BAD_FORBIDDEN,NET_RESPONSE_BAD_REQUEST,NET_RESPONSE_BAD_PAYMENT,NET_RESPONSE_BAD_UNAUTHORISED:
DebugLog("NET (GetAction): Permission refused: Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_ERROR
Case NET_RESPONSE_ERR_TIMEOUT:
DebugLog("NET (GetAction): Attempt timed-out: Response code: "+Str(ns_ServerResponseCode))
Case NET_RESPONSE_ERR_BANDWIDTH:
DebugLog("NET (GetAction): Server busy: Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_ERROR
Case NET_RESPONSE_ERR_NOTIMPLEMENTED,NET_RESPONSE_ERR_SERVER:
DebugLog("NET (GetAction): Server error: Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_ERROR
Default:
DebugLog("NET (GetAction): Unclassified Server error (Assume Failure): Response code: "+Str(ns_ServerResponseCode))
Return NET_RESPONSE_ACTION_ERROR
End Select
End Function

;JUST A WRAPPER TO AVOID INCREDIBLY LONG LINE OF CODE. THIS MUST BE ALL ON ONE LINE
Function NET_BuildHandshakeString$(s1$,s2$,s3$,s4$,sProtocol$=NET_PROTOCOL_HANDLE_HTTP11)
If ((s2<>"") And (s2<>"/"))
s1$=FixPath(s1,True,True)
End If
Local Send$=Replace(NET_LOCAL_SEND_GET$+" "+s1$+s2$+" "+sProtocol$+CARRIAGE_RETURN$+"Host: "+s3$+CARRIAGE_RETURN$+"User-Agent: "+s4$+CARRIAGE_RETURN$,Chr(0),"")

Return Send
End Function

;RETURNS URL PROTOCOL PREFIX
Function NET_GetProtocolPrefix$(snt_Path$)
Local ns_Inst=Instr(snt_Path$,NET_PROTOCOL_SUFFIX)-1
If (ns_Inst)
Return Left(snt_Path$,ns_Inst+Len(NET_PROTOCOL_SUFFIX))
End If

Return ""
End Function

;RETURN IDENTIFIED PORT
Function NET_GetProtocolPort(snt_Path$)
Local ns_Port=NET_PORT_DEFAULT_HTTP_PORT

Local s_Protocol$=NET_GetProtocolPrefix(snt_Path)
If (s_Protocol<>"")

s_Protocol=Left(s_Protocol,Instr(s_Protocol,NET_PROTOCOL_SUFFIX)-1)
If (Lower(s_Protocol)=Lower(NET_PROTOCOL_HTTPS))
ns_Port=NET_PORT_DEFAULT_HTTPS_PORT
End If
End If
Return ns_Port
End Function

;RETURNS DOMAIN PATH WITHOUT HTTP PROTOCOL
Function NET_TrimProtocolHead$(snt_Path$)
Local snt_Trim$=NET_GetProtocolPrefix(snt_Path)
Local snt_Sector$=Right(snt_Path,Len(snt_Path)-Len(snt_Trim))

;Trim www. where applicable
; If (Left(Lower(snt_Sector),4)=(NET_WORLDWIDEWEB+"."))
; snt_Sector=Right(snt_Path,Len(snt_Path)-Len(snt_Trim)-4)
; End If

Return snt_Sector
End Function

; THIS IS THE MAIN DOWNLOAD FUNCTION AND CONTAINS DOWNLOADING STREAM TO FILE WRITING
Function NET_DownloadFile(snt_FullServerPath$, savepath$ = "", savefile$ = "",s_Protocol$=NET_PROTOCOL_HANDLE_HTTP11, nb_IgnoreNullFilename=False, Agent$=NET_USERAGENT)
;Strip protocol and return false if not "http"
Local sdr_DownloadPath$=FixPath(FixPath(savepath,True,False)+savefile$,False)

;Seperate Sector Path and FileName from original Server Path
;Seperate path and file from the link
Local nIterBytes
Local snt_ServerSector$=""
Local snt_ServerFile$=""

For nIterBytes = Len(snt_FullServerPath) To 1 Step -1
If (Mid(snt_FullServerPath,nIterBytes, 1) = "/")
snt_ServerSector$ = Trim(Left(snt_FullServerPath,nIterBytes))
snt_ServerFile$ = Right(snt_FullServerPath$, Len(snt_FullServerPath) - nIterBytes)
Exit
EndIf
Next

If ((snt_ServerFile = "") And (nb_IgnoreNullFilename=False))
;NULL FILENAME
DebugLog("NET (Download): Filename is null and no permission granted for null Filenames. Abort streaming attempt")
Return False
End If

Local snt_Domain$=NET_RetrieveDomain(snt_FullServerPath)

Local hst_TCPStream=NET_CreateTCPConnection(snt_FullServerPath)

If (Not(hst_TCPStream))
; NO TCP CONECTION
DebugLog("NET (Download): Connection could not be Established")
Return False
End If

DebugLog("NET (Download): Connection Established 0x"+Str(hst_TCPStream))

Local s_Send$=NET_BuildHandshakeString$(snt_ServerSector,snt_ServerFile,snt_Domain$,Agent$,s_Protocol$)

DebugLog("NET (Download): Attempting Handshake Negotiation")
DebugLog("NET (Download): "+Replace(s_Send,CARRIAGE_RETURN,"|"))

WriteLine hst_TCPStream,s_Send


Local ns_Reply=NET_ResolveServerResponse(hst_TCPStream)
Local nb_Action=NET_GetActionByServerResponse(ns_Reply)
Local nb_Success

Select (nb_Action)

Case NET_RESPONSE_ACTION_DOWNLOAD:
DebugLog("NET (Download): Continue to read from Stream..")
nb_Success=NET_DownloadFromStream(hst_TCPStream,sdr_DownloadPath)

Case NET_RESPONSE_ACTION_REDIRECT:
DebugLog("NET (Download): Stream authorised, but content relocated. Seeking Location: header")

Local NewPath$=NET_LocateRedirectionPathFromStream(hst_TCPStream)

If (NewPath<>"")
NewPath=FixPath(NewPath,False,True)
If (NewPath=snt_FullServerPath)
DebugLog("NET (Download): Redirect destination matches initial content target. Aborting to prevent infinite loop")
Else
DebugLog("NET (Download): Attempting to connect to redirect destination: "+NewPath)
Return NET_DownloadFile(NewPath,savepath,savefile,s_Protocol$,nb_IgnoreNullFilename,Agent)
End If
Else
DebugLog("NET (Download): Content redirection path is Null. No valid location")
End If

Default:
DebugLog("NET (Download): Unable to continue.")
NET_CloseTCPConnection(hst_TCPStream)
End Select

If (nb_Success)
If (FileType(sdr_DownloadPath)=1)
If (FileSize(sdr_DownloadPath))
DebugLog("NET (Download): Stream Read completed: "+FileSize(sdr_DownloadPath)+" bytes")
Else
DebugLog("NET (Download): Stream Data empty")
DeleteFile sdr_DownloadPath
nb_Success=False
End If
Else
DebugLog("NET (Download): Stream Read Was successful, but local Data could not be written")
;DOWNLOAD HAS FAILED
DeleteFile sdr_DownloadPath
nb_Success=False
End If
Else
DebugLog("NET (Download): File Stream Read Failed")
; DOWNLOAD HAD FAILED
DeleteFile sdr_DownloadPath
End If

Return nb_Success
End Function

;~IDEal Editor Parameters:
;~C#Blitz+

The "BlitzGET" and Download examples in the code archives, do not seem to be compatible with BlitzMax NG and my attempts to learn more from https://blitzmax.org/docs/en/tutorials/tcp_tutorial/ hit a bump because it always returns "BAD REQUEST"

Const pref1:String="https"
Const pref2:String="://"
Const domain:String="www.syntaxbomb.com"

Local hints:TAddrInfo = New TAddrInfo(AF_INET_, SOCK_STREAM_)'AF_INET_ = IPv4  -- or AF_INET6_ = IPv6 -- alternatively, use AF_UNSPEC_ for either protocol ---------------------- SOCK_STREAM = SOCK_DGRAM_ for UDP

Local socket:TSocket = TSocket.Create(hints)


If Not socket Then
    Print "Unable to create socket."
    End
End If

Print "Socket Created"

Local host:String = domain
Local port:Int = 80
Local infos:TAddrInfo[] = AddrInfo(host, port, hints)


If Not infos Then
    Print "Hostname could not be resolved."
    End
End If

' we'll try the first one
Local info:TAddrInfo = infos[0]

Print "IP address of " + host + " is " + info.HostIp()

'Connect() method returns true if successful

If Not socket.Connect(info) Then
    Print "Error connecting to remote"
    End
End If

Print "Socket connected to " + host + " on ip " + socket.RemoteIp()


' send some data to the remote server
Local message:String = "GET / HTTP/1.1~r~n~r~n"

If Not socket.Send(message, Size_T(message.length), 0) Then
    Print "Send failed."
    End
End If

Print "Send successful"
' now receive some data
Local buf:Byte Ptr[1024]
Local dataLen:Size_T = socket.Recv(buf, 1024)

If Not dataLen Then
    Print "No data"
    End
End If

Print String.FromUTF8String(buf)
socket.close


Example:
QuoteSocket Created
IP address of www.syntaxbomb.com is 213.175.217.229
Socket connected to www.syntaxbomb.com on ip 213.175.217.229
Send successful
HTTP/1.1 400 Bad Request
Date: Thu, 02 May 2024 09:30:12 GMT
Server: Apache
Accept-Ranges: bytes
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Connection: close
Content-Type: text/html

_________________


Is there an example of a means to send http request and download the html content at a given url ?