Posts Tagged ‘netplay’
I’d been meaning to write this a lot earlier, but I’ve been way too busy to do it until today. Anyways, I made an online persistent world game in 48 hours. I wonder if it’s been done before during LD? We really need a tag system for games or something, it’d be interesting to be able to look up games with
specific elements. This post will be a bit of a combination of a making-of and a post-mortem; I’ll explain how this game came to be and comment on it.
Before Ludum Dare
This was my second Ludum Dare, so I had a decent idea of what to expect. Last time I used Unity, which had both its advantages and disadvantages. But since I’m the kind of person who wants to be able to customize everything and doesn’t want unnecessary things forced into his game, I prefer not to work with Unity. Fortunately, since February I’ve been doing an internship where I’m working on an XNA game, so I got pretty familiar with it. In making that game, I wrote a simple sprite engine which turned out to be very useful, so a week or so before LD23, I took the engine, removed some game-specific functions, added a tiny bit of documentation and released it as VBXSE. A short while ago I’d also been working on a simple netplay engine named GNI in my free time, so I decided to release that as well.
The original idea
After waking up at around 9-10 AM (competition started at 3 AM for me, but sleep is very important if you’re doing a 48-hour solo project), I checked the site to find out the theme was unexpectedly ‘Tiny World’, a theme I hadn’t even considered a possible winner in the poll. After thinking about it, I decided to make a roguelike-ish survival game similar to UnReal World where you and a couple of NPCs were stuck on a tiny island and had to get food and shelter to survive. That’s right, the original concept was nothing like what the game eventually became. I wrote down the basic game design in a text file, if you want details. So, I started working on the survival roguelike…
World and Graphics
Since roguelikes tend to be played in console windows, I decided to keep the area size small enough to fit in one. After some thinking, I decided square areas would make the most sense considering you also need room for other game information, and I ended up at a 22×22 world with each tile having 22×22 subtiles.
After a rough interface sketch, some calculations and some guesswork I decided the tiles would graphically fit best at a 32×32 size. However, I’m terrible at drawing stuff, so I decided to turn ‘lack of skill’ into ‘style’ and went for a pixel art look; I drew every tile and item in 8×8 and then resized it to 32×32 to make it look ‘retro’.
I made all of the art in GIMP because the Paint that comes with Windows 7 tries to be too hard to be a real drawing program and in doing so actually becomes worse for making pixel art…and it still doesn’t have any transparency. Brushes were surprisingly actually very useful when making tiles like grass, sand and sea. Just keep using random brushes and you have good-looking grass. Definitely something I should use more often.
Sound and music
The music for the game was made in FL Studio 9 using only soundfonts from DSK Music‘s HQ Instruments set. Although the effect is very subtle, the game actually contains dynamic music; although throughout the entire game the same track keeps playing, there’s three slightly different versions of them. They play simultaneously, and the volume adjusts depending on where you are.
The music used the ‘Celtic Harp’, ‘Ney Flute’, ‘Percussion 1′, ‘Oboe’ (forest only) and ‘Harp’ (beach only) soundfonts. It was inspired by Paavo “Tarantula” Harkonen’s soundtrack for obscure MMORPG Dransik (now a shadow of its former self and named ‘Ashen Empires’) and a random street performer playing on a harp the day before LD23.
The sound was created by rubbing or hitting various objects around my desk against eachother in different ways, recorded with my laptop microphone and edited in Audacity (amplitude change, pitch change and echo). Like the music, it was mainly inspired by obscure MMORPG Dransik, where you would hear a simple but satisfying sound whenever you did things like cutting trees and mining.
But how did it become a persistent world game?
Development started out well. I started by making simple ‘world generation’ (filling the entire world with grass tiles). Then I made movement work, then proceeded to add sea and forests to world generation. I added the axe and item usage, so you could chop wood. Then I also needed support for dropping and picking up items, which also required a menu to let you choose between items. I also made sure you could save and load your world, so I made functions to serialize the entire world to a long string and load it again.
At that point, day 1 was already over (LD goes from Saturday 3 AM to Monday 3 AM here, so it’s 2 days instead of 3 as it is in some time zones), and there was no sign of crafting, construction, the hunger system, NPCs, wildlife, fishing and similar methods of food gathering, liquids, an age system, building recognition or pretty much anything beyond the very basics of the game. Whoops. So, what is the logical thing to do when your plans seem way to ambitious?
I went with the logical solution: I changed my game into a complete persistent world building game. Of course it’s a ridiculous decision to take halfway through development, and even more so when your project is in that state due to being way too ambitious, but all things considered, it ended up being not as unreasonable as it sounds. I had recently created a netplay library, and though network communication is always tricky and it was not tested much, it did seem to work perfectly from the small amount of testing it did get. As a game design decision it was more logical than anything; I was just a tiny bit of code away from making a game where you can build stuff, and games like Minecraft, Terraria, Active Worlds, a whole slew of BYOND building games and many more games have pointed out that just building things for other players to see can actually be fun in itself. Add to all that that I could already serialize any part of the world to a string, and the decision was made overnight to go all-or-nothing for an online persistent world game.
The first thing I did on the second day was finish the crafting and building system (otherwise even having a persistent world would be futile). Then I used my GNI library to write a server program and a client class. The client would ask the server for the serialized string representing the world map, and then for the serialized strings of each of the areas in the tiles (each tile on the 22×22 world map contains an inner area consisting of 22×22 ‘subtiles’, if you haven’t played the game). I found out the serialization had some errors, but after some bugfixing, it actually worked perfectly. Then, to make client changes affect the server and have the server keep the client up to date, instead of at the start the server would send the client the inner areas only whenever he zoomed in or walked to a different inner area, and the client would serialize the entire inner area and send it to the server whenever a tree was cut, an item was dropped or picked up and whenever something was built. It’s an inefficient approach (the reason it lags whenever you switch between areas or do something area-affecting), but it worked great for a 48-hour game. I then proceeded to add functionality for chat, player names, seeing each other, et cetera.
After the netplay features worked, I decided to add what little extra content I could still safely push in (mountains, stone, and anything that requires stone – yes, originally there was only wood) and release it.
What I like about the game
-It’s a persistent world game. That by itself is awesome.
-It’s my first succesful attempt at a non-BYOND online game. There was one other finished game were I attempted netplay, but its netplay was a horribly buggy mess that desynced for any players that were more than 2 meters apart.
-The world generation is nice. I’ve barely done any random world generation before, so the fact that I managed to get a properly shaped island with properly shaped beach and mountain areas out of it is pretty nice.
What I dislike about the game
-Lack of content. And I mean utter lack of content. There’s two kinds of walls to build, two kinds of floor plus four natural floor tiles, one tool to make and one decorative item. Ludum Dare games aren’t known for their extreme length, but it’s very minimal here; it’s a building game, but there’s hardly any variation in what you can build, so you get bored very easily. This is especially disappointing after my previous LD game, which could be played for significant lengths of time and still be interesting.
-It’s buggy and laggy. Whenever you zoom in, lag. Whenever you build something, lag. Whenever you cut a tree, lag. Whenever you move from one area to another, lag. And those 4 natural tiles I mentioned at the previous point? Good luck getting three of those without relogging a minute later, as they mess up something in the graphics (still not sure what causes it).
Things I should do again next LD
-Use VBXSE and GNI again. Especially the latter came as a surprise in how powerful it can be in just a small amount of development time. I was afraid of netplay functionality before considering the debugging hell it can cause, but now I think I’ll just plan for netplay from the start of the theme allows it.
-Use pixel art. I can’t draw, but ‘style’ turns out to work as a pretty good excuse to hide my lack of skill.
-Record random objects using my laptop microphone and edit them in Audacity for quick sound effects. I’m pretty satisfied with how they turned out; they subtly add a lot.
-Be overly amibitious. Partly because I’m just an idiot, but also because going beyond what you know you can do allows you to learn new things. It would be better for the game if I didn’t, but in the long run this is very useful.
Things to keep in mind for next LD
-I should make sure there’s enough content. LD is for games, not for tech demos.
-I should try to get the day after LD off work. Starting a week with a serious lack of sleep hurts your productivity for the entire week.
-I should update and improve VBXSE and especially GNI. They’re awesome, and therefore they must become even more awesome.
-I should make sure I’m used to the the tools and libraries I’m working with. I thought I knew XNA, but it wasn’t until halfway through the project that I realized I didn’t know even how to play multiple music tracks at the same time and change their volume. I also wasted a lot of time debugging an issue that happened because I didn’t take into account that using auto-poll GNI’s client class handles received data in a different thread.
I was initially considering updating the game a bit, adding some extra content and fixing some things I didn’t like, but there are so many things I’d like to change and so many things that need to be changed to make the game actually fun that it’d just take too much time. I think it would be interesting to one day make a complete game based on this, but that’s the kind of plan that will either never see the light of day at all or won’t be used until years later.
By the way, I forgot to do it initially, but I’ve slapped a Creative Commons license on the game so you have the right to mess with it in any way you want. Well then, see you all next LD!
Appendix A: Tools/libraries/etc used
C#: The language the game is written in.
XNA: Engine used for the game.
Microsoft Visual Studio 2010: The IDE I’ve used to make the game in.
VBXSE: XNA Sprite engine, used for all graphics stuff.
GNI: Netplay library, used for client-server communication
FL Studio 9: Used to make the music. I’m a fervent supporter of pattern blocks, even as Image-Line gets rid of its unique features.
DSK Music’s HQ Instruments: Set of amazing soundfonts I use for music. The music for this game was made solely using DSK HQ Instruments, using default FL Studio VSTs as effects.
Audacity: Pretty much the best audio editor around. Used for sound effects.
Appendix B: Network signals
If you’re interested in how exactly the server and client communicate, these signals are sent back and forth. Each signal has a ‘key’ denoting what the signal is about, and a ‘value’ containing other info.
Server to client
(None) – A signal with no information to check if the client is still connected.
identify – Asks the player for his appearance and location, in case some other players requests it later.
goplay – After version check and sending the necessary maps. Tells the client the player can start playing.
gspfinish – Indicates the server is finished sending inner tiles and the client can enter the subterrain the player was about to enter.
message – A chat or system message. Output the value to the displayed messages.
people – A list with the unique player numbers of all players that are currently online. If a known ID is missing, that means a player has disconnected. If there’s an unknown ID, the client sends a ‘who’ signal to ask for information on the new player.
person – Information on a player (ID, appearance, location) as a response to the ‘who’ signal. The client will then add the player to the list of known IDs.
pos|X – Where X is the unique player number of a different player. Tells the client where the specified player is right now.
subter|X|Y – Where X and Y are integers representing coordinates. Replace the inner tile at X,Y with the serialized inner tile in the value string.
subteru|X|Y – Same as above, but the change is vital, so if the player is in the area, it MUST update the inner area before letting the player continue doing whatever he’s doing.
versioncheck – Asks the client to send his version number, to make sure he isn’t using an outdated client.
versionmismatch – Tells the client he can’t play on the server because the versions don’t match. Which version the server is running is in the value.
where – Requests the client to send a ‘here’ signal, telling the server where the player is.
worldmap – Sends a serialized world map as the value. Allows the client to build the same world map client-side as the one that exists server-side.
Client to server
The server automatically sends these signals without the client saying anything to it:
Every 2 seconds – where – Ask for the player’s current location.
Every 2 seconds – pos – Tells the player where other players are.
Whenever a client connects – versioncheck – Asks the player for his client version.
The server can receive the following messages:
cmsg – Client wants to send a chat message.
getsubplus – Value contains X and Y coordinate. Client asks server for the inner area at (X,Y) as well as the 8 areas around it. Server sends them followed by a ‘gspfinish’.
here – Client tells the server where the player is.
ident – Client tells server information about the player (appearance and location). Server stores the information and sends ‘worldmap’ and ‘goplay’ signals to let the player start.
nick – Sets the player’s nickname.
usub|X|Y – Client tells server they updated the inner tile at (X, Y). Server updates world and sends a ‘subteru’ signal to all players.
version – Client tells server what version they’re using. If it matches, the server will return an ‘identify’ signal, otherwise it will return a ‘versionmismatch’ signal.
who – Client asks server for information on a player. Server sends a ‘person’ signal in response.
I probably won’t use this next weekend, but I’m posting this just in case I do want to use it.
GNI is a library to facilitate some simple, easy to use communication between a server and any number of clients.
Please note that this was coded in a relatively short time span, it was not tested extensively, network code is vulnerable to tons of unpredictable bugs, and my experience with netplay development is limited.
What I want to say is, don’t rely on it unless you’re capable of fixing bugs in it, as it might break at the worst possible moment.
The example chat program I wrote seems to work perfectly, though, so it should work. ‘Should’ being the key word here.
-Simple way to send signals from server to client and vice versa, without bothering with minor technical details
-Automatically calls a function when a signal is received, a player connects, or a player disconnects
Code includes a simple chat server project and chat client project as examples.
How it works
Small packages of information called GNIDatas are sent through the library. When received, it’s reconstructed as a GNIData struct.
GNIData consists of a key-value pair, with the keys or values being shorts (16-byte integers), strings, or nothing.
-The server class must inherit from the library’s GNIServer class.
-Call StartServer(…) to start the server. You can have it check for incoming signals automatically, or choose to do so manually by calling Update().
-Override OnDataReceived(GNIData data, uint source) to interpret signals. It’s called from Update when a full GNIData has been received. ‘Source’ is the clientID of the client that sent the signal.
-Override OnClientConnected(GNIClientInformation client) if you want something to happen when a player joins. (The client is automatically added to the client list.)
-Override OnClientDisconnected(GNIClientInformation client) if you want something to happen when a player disconnects. (The client is automatically removed from the client list.)
-Use SendSignal(…) to send a GNIData to a client, or BroadcastSignal(…) to send a GNIData to every client.
-Send empty signals (datatype and valuetype ‘none’) if you want the server to automatically detect if a connection has been lost; otherwise it won’t notice until it fails to send a signal to it.
-The client class must inherit from the library’s GNIClient class.
-Call StartClient(…) to start the client. It WILL NOT check for incoming signals automatically; call AutoPoll() to have it do that. Alternatively, call Update() whenever you want to check for incoming signals.
-Override OnDataReceived(GNIData data, uint source) to interpret signals. It’s called from Update when a full GNIData has been received. ‘Source’ is always 0 and meaningless in the client.
-Use SendSignal(…) to send a GNIData to the server.
-To be honest, I’m not sure what happens when the client unexpectedly loses connection to the server. It’ll probably throw an exception, so just catch that.
Does not exist. If you can’t figure out the code, it’s best not to use it as it might still be buggy.
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
You are free:
* to Share — to copy, distribute and transmit the work
* to Remix — to adapt the work
* to make commercial use of the work
Under the following conditions:
* Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
Have fun, and good luck with your games.