Hero image!

I made a small geography game

January 15, 2024 - Drygast
Fly.io Game Dev Go(lang) Javascript Software Dev SQLite

The concept is easy - a video is shown and it's your job to find the position of the person filming and mark it on a map within a certain time.

First of all - it is pretty much a copy of another game, but that game has not been playable for me and my friends for quite som time. We tried every friday for more than 6 months, but in the end we could only assume that the multiplayer part of that game was abandoned. So I made my own version of it.

Obviously it would be a lot more satisfying to come up with the idea myself, but I guess my perfect idea is still lurking somewhere. Until that idea surfaces, I don't mind actually playing a fun game. This type of game is perfekt for a short "teambuilding" thing or a short battle between friends.

If you want to try it, go to https://wandrlust.games and start a single or multiplayer game. The multiplayer part is working OK, but it is not meant to be able to scale above a single instance so it will probably fill up pretty quickly if people find it.

Websockets

OK - so let's start with some tech stuff. I knew that I had to create some sort of server to handle the main game operations and after a little while I descided to create a small golang server that hosts the static webpages and also a websocket server for the game messages. My idea was to simply create a couple of static webpages for the necessary HTML and images and then handle all client functionality in javascript. The reason for this is simply that I have done something similar before and thought it would be quite quick to build something like that again.

Building a basic websocket server in golang is really easy and there is a lot of tutorials out there, so I'm not going to post much code for this one. Here is the main function with a few things removed:

func main() {
	port = os.Getenv("PORT")
	if port == "" {
		port = "9009"
		log.Printf("defaulting to port %s", port)
	}
	if port == "9009" {
		isDebug = true
	}
	// Create a simple file server (used to host the javascript client)
	fs := http.FileServer(HTMLDir{http.Dir("public_html/")})
	http.Handle("/", http.StripPrefix("/", fs))
	http.HandleFunc("/randomlocation/", randomLocationFromDatabase)
	http.HandleFunc("/randomlocations/", randomLocationsFromDatabase)
	// Configure websocket route
	http.HandleFunc("/ws", handleWsConnections)
	// Start listening for incoming messages
	go handleWsMessages()

	// Create the ticker that will act as the gameengine to handle timeouts and cleanups and other stuff
	ticker := time.NewTicker(1 * time.Second)
	quit := make(chan struct{})
	go func() {
		for {
			select {
			case <-ticker.C:
				gameTicker()
			case <-quit:
				ticker.Stop()
				return
			}
		}
	}()
	log.Println("server.go:main() - http server starting on port:" + port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatal(err)
	}
	if errors.Is(err, http.ErrServerClosed) {
		log.Printf("server closed\n")
	} else if err != nil {
		log.Printf("server.go:main() - error starting server: %s\n", err)
		os.Exit(1)
	}
}

Connecting to that server in javascript is also very easy so verifying that the basic setup would work took no time at all. I added a 20 second continous ping to make sure that I held the connection open.

socket = new WebSocket(host);
socket.onopen = function(msg) { 
   pingIntervalId = setInterval(ping, 20000);
};
socket.onmessage = function(msg) { 
   handleMultiplayerMessage(msg.data);
};
socket.onclose = function(msg) { 
   clearInterval(pingIntervalId);
};

Database

At first I though about using a more capable SQL database, but pretty soon I realized that I did not need any fancy stuff - SQLite would work just fine. I only needed a few tables to hold the different locations where the Youtube video id, timestamp and lat/lon location was stored. I also wanted to add countries and continents so that I could limit the locations for a round. I also added categories for the same reason. Most Locations are in the category WALKING, but I think monument/landmark have a few as well. I will try to add Locations to the other categories (driving/biking and static (no moving) over time as well).

Hosting

This is where the first hickup occured. Or rather - I built the entire thing and just assumed that it would work the same way as it works locally and my initial idea was to host this little game on google cloud. After I created everything necessary in google cloud I noticed that I was kicked out from the server after a certain time. It turns out that google cloud shuts down active connections after a certain time, and even if it is possible to increase this time I think the max time was an hour with 20 minutes default (not sure about those values). At the same time I read about other people that hosted websocket servers on google cloud and they pretty much warned against it due to possible massive bills... What it all boiled down to was that I found https://fly.io and that is where the game is currently running. So far I'm very satisfied with how it works.

Final thoughts

I'm not done with the game or the site yet, but it is possible to play at the moment. Go to https://wandrlust.games and you can try it out for yourself. I dont really know what else to write about this if I'm honest - I just wanted to get an article out about the game in it's current form. I actually like playing it and even if I've seen most of the locations by now, I still forget where it is between rounds.

When I'm at the point of feeling done with this game, I might update this post and write a lot more about it, but this will do for now. My main goal at the moment is to iron out a few small bugs and try to prepare the site and game for some advertisment if possible. Not sure how that will go, but I'll report back when I know more.