It is a very common property of today's games—even those that are strictly single player—to connect to a server at some point in time to retrieve data such as the latest headlines from the game's official website, leaderboards, patches or the latest bonus content package. By the end of this recipe you will be able to do so as well.
Hosting files on a web server is very simple to set up and reasonably priced these days. Additionally, this makes it easy to use Panda3D's built in networking features. The engine comes with a class called HTTPClient
that hides the complexities of communicating with a web server.
In this sample you will learn how to connect to a server, download some data and display it on the screen.
This recipe will use the framework presented in Setting up the game structure found in Chapter 1, Setting Up Panda3D and Configuring Development Tools. Please set up your project prior to going on with the following tasks.
Complete these tasks to make Panda3D download files from a server:
Application.py
and fill in the following code:from direct.showbase.ShowBase import ShowBase from panda3d.core import * from direct.gui.OnscreenImage import OnscreenImage class Application(ShowBase): def __init__(self): ShowBase.__init__(self) self.http = HTTPClient.getGlobalPtr() self.channel = self.http.makeChannel(False) self.channel.beginGetDocument(DocumentSpec("http://www.panda3d.org/images/panda-logo-2.png")) self.channel.downloadToFile(Filename("panda.png")) taskMgr.add(self.downloadData, "downloadData") def downloadData(self, task): if self.channel.run(): return task.cont if not self.channel.isDownloadComplete(): print "Unable to download file." return task.done else: OnscreenImage("panda.png", scale = Vec3(0.2, 0, 0.2)) return task.done
Now that our little program is able to download an image file to disk and display it on the screen, let's take a look at the steps that are necessary to achieve this goal.
First, we retrieve a reference to the global singleton instance of HTTPClient
that Panda3D creates for us automatically. We could also create a new instance here, which wouldn't alter anything about this program's behavior apart from a minimally increased memory footprint.
We then go on to create a new HTTPChannel
object by calling the makeChannel()
method on our HTTPClient
. This channel object represents the communication line to the server over which we will retrieve the image file. We also pass False
as an argument, which makes the channel non-persistent. This means that immediately after the requested file has finished downloading, the connection will be closed. This fits our use case pretty well as we want to retrieve only one single file from the server. If we wanted to download a series of files, we would use a persistent connection. This is more efficient as the connection is kept open and reused instead of opening a new one for each request.
The next two calls, beginGetDocument()
and downloadToFile()
, are used to set which file to request from the server and where to store it. We try to retrieve a file hosted on the website of Panda3D, but we could easily replace it with a URL that points to a different resource, hosted on another server. Note, however, how we need to hand the URL to a new DocumentSpec
object, which is responsible for handling whether the file can be retrieved from the local cache or needs to be downloaded.
By using downloadToFile()
, we configure our HTTPChannel
to download the requested data into a local file. Alternatively, we could also use downloadToRam()
to store the data in a Ramfile
object that represents a virtual file that uses the system's RAM for storage instead of the hard disk.
The call to beginGetDocument()
is non-blocking. This means that instead of waiting for the complete download to finish within the execution of this method, it places a request for the desired file. Then the call returns and the program continues execution from that point onward. To actually download data after requesting it, we need to periodically update our channel using the run()
method that is called periodically inside the downloadData
task. This task will keep on updating the channel as long as there is data incoming before checking if the download has finished. As soon as the file is retrieved to the local hard drive, a new OnscreenImage
is created to display the downloaded image.