The internet
A game can talk to a web server: fetch today’s news for the title screen, send
a score to a leaderboard, download a level someone made. It all goes through
one function, fetch.
Crash-tan’s Tip:
fetchworks in my players for PCs, Macs and phones, and not in a web browser, this book included. The last section says why.
Asking a server
fetch(url, method) sends a request and waits for the answer. It gives back
a map with two things in it: statuscode, the server’s answer as a number,
and message, what it sent back, as text. 200 means “here you are”, and 404
means “there’s nothing here”.
SCREEN 320, 240
answer = fetch("https://example.com/", "GET")
IF answer.statuscode = 200
PRINT "the page is", len(answer.message), "characters long"
PRINT "it starts:", left(answer.message, 15)
ELSE IF answer.statuscode = 0
PRINT "no answer:", answer.message
ELSE
PRINT "the server said", answer.statuscode
END IF
What that prints depends on whether you’re online right now. Every request
can fail, so every game that uses fetch checks what came back.
When nobody answers
A request that never reaches a server, because you’re offline, or the address
is wrong, or the server is down, doesn’t stop your game. statuscode is 0, and
message says why.
SCREEN 320, 240
answer = fetch("not a web address", "GET")
PRINT "statuscode:", answer.statuscode
PRINT "was there a reason?", len(answer.message) > 0
statuscode: 0
was there a reason? 1
I give a request 30 seconds. One that hasn’t been answered by then is a 0 too.
Crash-tan’s Tip:
fetchwaits for its answer, and while it waits your loop doesn’t go round. Fetch what you need before the game starts, or on a thread of its own withCALL THREAD, the way the next example does.
Receiving JSON
Most web servers that games talk to answer in JSON, and deserialize turns
that into maps and lists, the same as it does for a save file. Here a thread
fetches some JSON while the title screen keeps going:
SCREEN 320, 240
ATOMIC news = "loading the news..."
FUNCTION get_news()
answer = fetch("https://httpbin.org/json", "GET")
IF answer.statuscode = 200
data = deserialize(answer.message)
news = data.slideshow.title
ELSE
news = "no news today"
END IF
END FUNCTION
CALL THREAD get_news, "news"
DO
IF COOLDOWN(1)
FRAME
CLS
LOCATE 8, 12
PRINT "SUPER CRASH WORLD"
LOCATE 12, 4
PRINT news
END FRAME
END IF
LOOP
httpbin.org is a site for trying out web requests, and /json always sends
the same small piece of JSON. If it can’t be reached, the title screen says
so and carries on.
Sending JSON
To send something, use "POST" and give fetch a body. A map or a list is
sent as JSON, labeled as JSON, so a server knows what it’s getting. A fourth
value adds headers, which is how a server that wants a key gets one.
SCREEN 320, 240
score = {name: "Ada", points: 1200}
answer = fetch("https://httpbin.org/post", "POST", score, {"X-Game": "Super Crash World"})
IF answer.statuscode = 200
echo = deserialize(answer.message)
PRINT "the server got:", echo.json
ELSE
PRINT "the score didn't get there; try again later"
SAVETEXT "unsent.json", serialize(score)
END IF
httpbin.org’s /post sends back what it was given, so you can see the score
arrive. A real leaderboard would answer with your place on it. When it can’t
be reached, the score is saved, ready to send next time.
Other methods work the same way: "PUT", "PATCH" and "DELETE", for
servers that use them.
In the browser
fetch works in my players for PCs, Macs and phones. It doesn’t work in a
web browser. To run there, my web player needs the page it’s on served with
headers that keep it apart from other sites, and those same headers stop
fetch from reaching a server. So in a browser, every fetch answers with
a statuscode of 0, the same as a server that isn’t there.
That includes this book, which runs in your browser: Try the examples on this page and you’ll see the 0 path every time. That’s why every example here deals with a 0. A game that does the same keeps working in the browser, just without the internet.