Showing posts with label network. Show all posts
Showing posts with label network. Show all posts

Thursday, March 18, 2010

First killin's

All this week I've had the chance to test out and refine the network code of Space Combat Sim. I've also managed to get some of the guys from class to play the game and hash out bugs and glitches. Currently I've only been able to run small test sessions with four players but that's been enough to get a flurry of random bugs that needed fixing.

Here's an abridged list of SVN commits I've made since my last blog to give you an idea of the number of fixes and tweaks being done. They're in order from newest (yesterday night) to oldest.

  • Got the game closing down nicely now.
  • Mostly working game exit sequence... still doesn't unload DynamicTextures yet.
  • Precess rotation axes when in strafe mode.
  • Kill beams when its parent dies.
  • Fix wrong thrusters becoming visible.
  • Clean up ALL references to a leaving player (fix crash on join after a player quits)
  • Proper fix for weapon toggle not working on remotes
  • Minor improvement to weapon switching from remote
  • Fix scaling in billboard shader
  • Add state items for targeting, and thrust
  • Recursively update scene nodes as well as entities. (FIXED THE GLITCH)
  • Another attempt to fix off-by-one graphical glitch. Didn't work.
  • Ensure that entities on host will still receive remote input properly.
  • Don't collect input if using a remote controller
  • Implement bandwidth usage tracking and some measures to reduce usage
  • First stab at fixing crash on >2 players at once in game.
  • Fix wrong name in kill news item
  • Fix bug where more than one join attempt would cause a crash
  • Assign gamertag to player slot when initializing on remote.
  • Remember to dequeue pending notifications after sending them.
  • Finish up host side of sending player notifications
  • Implement remote side and part of host side of player notification packet.
  • Don't actively produce score updates unless hosting the game.
  • Fix kill detection bug.
  • First stab at fixing weird off-by-one-frame glitch.
  • Fix stack overflow in resolving entity creations.
  • First stab at queuing creations that arrive out of order.
  • Fix up absurd level of brightness.
  • Fix impossible to see through HUD
  • First shot at multipass lighting. Tested with single light
  • CoalesceDeadEntities called in response to entity destruction packet
  • Add weapon states to data to be sent.
  • Make sure criteria for StateItems being sent are updated.
  • Adjust send criteria to keep bandwidth more sane and keep lag down

After all the pain of February this switch from hacking and designing to steady bug fixing and feature creation is a very welcome change of pace. My most satisfying fix was to finally get rid of a bunch of annoying graphical glitches that occured when players would be killed and/or respawned. Their causes are rather boring so I'm not going to bother with any details.

Screenshots coming tomorrow. I might also talk about how to create a simple radar display.

Wednesday, March 10, 2010

Space Combat Multiplayer

Debugging network code for XNA is a huge pain in the arse! Typically when I'm bug hunting I'll place a breakpoint where I roughly think a bug is taking effect, look at the state of any relavent data, then step through the code until the bug manifests. This doesn't work in XNA. If you leave one player or the other player in the debugger for more than about ten seconds their network connection will drop. This means that I have to then reset the game and re-join the session. It gets quite frustrating spending an hour on a simple one line logic error with about ten minutes of that hour being spent actually debugging with the rest being hitting a breakpoint, checking variables for a few second and having the connection drop requiring yet another restart.

But it was worth it. After much pain multiplayer is now working. I got to show it off to the prof' this Monday. It's still buggy as hell but man, it's satisfying to be able to finally fly around and shoot other people. Even if getting a kill means crashing the program immidiately afterward.

As promised I'm going to talk about how I've implemented multiplayer so far.

The game uses a basic client/server architecture. Whoever starts the game acts as the server and has final say on stuff like where players are and who scored what kills. All other players are clients and passively receive data from the server. The only data sent to the server by the clients are an initial "I'm ready to receive data" signal and player input. The data sent to the players dictates stuff like what direction they're facing and how fast they're going.

There is a bit of hairyness here however. The client needs to know stuff like what graphics to use to display the game. If you remember my earlier entry on multiplayer, XNA allows a game session to have properties, in the form of integers, associated with it. By associating a particular play scenario with a number the client can find out what to load immidiately by checking the properties of the session he's joining.

The whole sequence of joining an active game goes like this

  • The client player chooses a server to join
  • The server notices the new client and creates an object to track what is to be sent to him
  • The client grabs the scenario number from the server's session properties
  • The client then runs the normal load sequence
  • After loading concludes the client tells the server it's ready to receive data
  • In response, the server sends the client basic information on all players (their score, team, assigned spawn points and so on)
  • At this point the client can begin playing. Normally they're waiting to spawn.

Wait... there's no state data sent yet. In fact, if nothing else were sent the player would see nothing except for some background scenery. The magic occurs in the object the server creates to track the data sent to the client.

Each client tracking object has a list of game entities which matter to the clients. The tracking object allows the server to know stuff like whether the client has been informed that the entity exists (or has ceased to exist) and if any state updates are pending. The server sends entity creation data first, updates second and entity destruction data last. The server can also check for certain combinations (e.g. neither creation nor destruction have been sent but both are pending) to save on bandwidth. In the case of the previous example the server would give up on sending either to the client since creation and destruction effectively cancel eachother out.

State updates are a little more complicated than creation and destruction of entities. These are managed by a class called StateItemCollection. StateItems are objects which describe stuff like an entity's position, movement, damage and so on. When some significant change occurs, say the player suddenly accelerates, the associated StateItem flags that interested players should be notified of the change. The client tracking object queries each entity's StateItemCollection for this flag. If any StateItem's flag is set then the server queues up an update for the client. Once the server has queued the update for all relavent players it clears the flag.

There are a couple advantages to using this flagging method. One is that I can allow the server to choose which players receive the state updates. This allows for the server to throttle itself if it's sending too much data, or to skip sending state data that isn't relavent to a particular player. Other advantages lie in the ability to compensate for lag and in merging updates by having later updates replace earlier updates if neither have been sent.

I'll leave you with this screenshot of one (poorly lit) player shooting grey bullets. Notice I have some (glitched) lighting working now :)

Friday, March 5, 2010

Networking Remark

Preview of my next entry:
Debugging network code in XNA Game Studio 3.1 sucks. I spent way more time than I care to think about restarting my game due to stupidity that has nothing to do with actual bugs in the game.

Monday, February 1, 2010

Loading and Networking

Soo... here's the blog I promised yesterday.

I'm not going to bother too much with background information in this entry. The main purpose of the loading sequence (aside from loading, obviously) is to separate the data needed for the player to set up a game (menu text and such) from the data needed to run the game. It also is meant to separate the initial synchronization sequence for players joining the game from the game's proper so the user need not deal with glitches as much.

In order to simplify content development I made the load sequence as intellegent as possible. When a player chooses a scenario (or a server to join) the game uses a custom content processor to read up data on stuff like team objectives, scoring, spawn points and such. Spawn points are defined such that they always spawn the same type of object. The object type is defined by an entity group type file, the custom content I'm writing about in my Custom Content Processor series, which is referenced in the spawn point. All of the scenario content data is wrapped in a single class.

With the scenario content loaded the game starts the loading sequence. First a function called GetDependentAssets, a member of the scenario content class, is called. This builds a set of all unique assets referenced by the scenario file. This set lives in the aptly named AssetCollection class.

Now, this initial list isn't the whole story. Entity groups in particular depend on other assets to render them in the game world. But I don't need to worry about that before loading assets because of some very simple magic built into the load function for entity Asset instances in AssetCollection. An Asset's Load method has access to the AssetCollection's Add function. If an asset in the collection depends on another asset it simply calls the Add function which will append the dependency to the AssetCollection if it's not already there.

This provides two advantages. First, I can rapidly develop scenarios since I don't have to alter the game's code to ensure all assets referenced by the scenario get loaded, second I have a very clean way of providing feedback to the user of how long the loading sequence will take. As I implied earlier each asset is loaded with an individual Load call. Once the load sequence runs out of assets that require a Load call the basic sequence is done.

Now what?
Well, if this is the player hosting the game we're good to go. Fire up the network session and let the player play. But if the player is joining an existing game the situation is a bit more hirsute.

Why? Because, as a joining player I know nothing about the game other than a few simple properties which can be expressed in a small set of 32 bit signed integers. Obviously this isn't much of use, especially since these properties don't really change after starting a game. (As far as I know anyway) I decided that I'll pack the initial scenario selection and its custom scoring/timing/whatever settings. This is enough for the joining player to run the load sequence above without further communication with the game's host.

After the load though there's a LOT the player needs to know before they can participate in a meaningful manner.
  • What team is the player on
  • Where/when will they spawn and as what
  • Who's on the other team(s)
  • What's the score, how much time is left etc.
  • What objects exist in the game, where are they, what do they look like
  • What objects belong to what players

So, this is where I am now. It's a fun problem to solve and I have a basic protocol thought up but it has yet to be tested. I'll update on it once I've had a chance to test things.

Later doodz

Sunday, January 31, 2010

Don't Worry, I'm Still Here

Sorry about missing last week.
I'm busy as hell working at the final (though it will probably change still at this stage) game startup sequence. I've got things working nicely now and will have a more detailed update soon. The load sequence is somewhat interesting and I think worthy of a couple paragraphs on this humble blog... later.

I'm also busy with revamping, and hopefully near-finalizing, the network protocol for the game. I'm giving particular attention to the sequence of events and data transfers that occurs when a player first joins the game.

If you remember in a previous entry I mentioned how this was the most time-consuming part of an earlier sprint. Given some further (and much briefer thought) I've created a much improved process and am throwing away pretty much all of my old network code in favour of a new solution which should support all of the events that may occur in the final game. All of this I'll write about, in more detail, tomorrow.

Off topic for a second...
I was visiting my parents earlier today for some delicious chicken and potatoes. I also got to see a bit of my brother's new score in gaming. He's picked up a copy of X3: Terran Conflict. This was a game that I've been moderately interested in acquiring myself. Unfortunately I only got to see a bit of a tutorial mission which was mildly, but irritatingly, broken. My brother was piloting an unarmed freight ship but the instructor, inexplicably, was telling him to blow up debris containers despite being unarmed. Needless to say he had to abort the tutorial at this point... going without any remaining information that could have been provided.

I hung around for a few more minutes. What I saw implied a large, freeform game universe. I'm strongly reminded of EVE Online, Freelancer and/or (what I've heard of) Privateer. Sadly, I didn't get to see much more since my brother seemed more interested in reading about bread factories than exploring the world.

However, based on what I've seen I'll definately look to pick up a copy for myself some time. The best part is I don't have to worry about Starforce, SecurROM (sp?) or any other bullshit DRM. I can either get it through Steam or pick up a DRM-free box somewhere. Even if the game turns out to be less cool than I'm thinking it will I'll still be more than happy to support a developer who's willing to not include pseudo-malware in their products.

Sunday, November 8, 2009

I can haz multiplayer test???

Over the last week I've been working on getting the network code implemented for Space Combat Sim. This is one of the most critical parts for the game since without it there's no way for the game to support multiplayer over Live.

This has turned out to be painful in one place which I didn't expect, the game's rules system. When I first created the game I've been working with a basic startup sequence which creates a bunch of fighters and assigns a local controller (i.e. your X-Box 360 gamepad) to one of them leaving the rest uncontrolled. I originally wanted to create a fairly simple stub and hook joining players to other objects arbitrarily. This didn't work.

The problem is that when a player joins they initially know nothing of the game's state. This includes which fighter they're supposed to control. If I just assigned one arbitrarily on both sides the results would be inconsistent. Each player would think that the other is attached to a different object. Worse, two players could be trying to control the same object. This meant I had to implement code which runs on the game's host only to assign a player to a specific in-game object. In addition it had to support the player being killed and assigned a new object of (possibly) a different type than before.

What occurs now is this:
  1. The host starts up a game session and starts playing.
  2. Some other player joins the game session
  3. The host notices the joining player and asks the rules system that a new player is in the game. This is where team balancing etc. would occur.
  4. In the mean time the joining player is watching the game without participating.
  5. The host notices that there's a player without a ship (i.e. the joining player.) The host finds an appropriate place to create the player and adds them to the game. In addition the host lets all players know that one of the players has just been assigned a new ship. This is where re-spawn rules would go.
  6. The joining player notices that they've been assigned a ship. Since it doesn't exist on their machine yet they create it and attach their local controller to it. The player is now in the game.
  7. If there were other players they'd also notice a player has been assigned a ship. Since it also doesn't exist for them they'd create it as well.

I've omitted some housekeeping but this is, at least for now, how players joining and being assigned ships is handled. What's left is testing. Since I only have one PC on hand I'll need to do that at school.

Sunday, October 25, 2009

And Now for Something Completely Different

For the last week or so I've been working on getting weapons firing. This included a lot of architectural crap which I mentioned previously. Weapons are working quite nicely now, though bullets will keep flying off to infinity forever currently. The other issue are some obnoxious visual defects whose cause is a mystery currently.

Weapons have some basic rules which govern whether or not they're allowed to fire. First, the gun must have ammunition. Second, the gun must be ready to fire. Every weapon has to rest briefly between shots. In real life this is because it takes time for a new bullet to be loaded into the chamber and for the firing pin to be cocked. Finally, the gun must be cool enough to fire. I decided to take a page from the Mechwarrior games here. Every weapon produces some amount of heat. When this reaches a maximum the player must wait for the weapon to cool off again. Effectively the player's firerate is dramatically cut when the weapon overheats. Between heat, and limited ammo, the player should be encouraged to fire in controlled bursts for maximum damage.

Today I decided to switch gears. Since this phase is supposed to produce the first multiplayer implementation I started coding in Live support.

XNA provides network related code in two major chunks. First, GamerServices, which is where most of the Live service code lives. Second is Net, which handles all the grunt work of communication between players.

The first thing I figured I should tackle is allowing the player to sign in to the Live service. This is done through a program/widget built in to the X-Box 360 called the Guide. As it turns out this required about three lines of code. Two for some setup and the third, which does the real work.
Guide.ShowSignIn(1, false);
Seriously! That's all that's needed to fire up the Live signin screen. The 1 states that only one player can be signed in for this game. The second indicates whether only online Live profiles can be used.

Next task is to get hosting and matchmaking up and running. Thusfar it looks pretty simple. The hard part looks to be the actual work of communication between players.

Wednesday, October 14, 2009

The Second Nastyish Problem

I now have the critical part of the batching portion working. Objects are decomposed to their smallest constituant parts which can then be rendered in groups. The actual grouping is still to be done but that's relatively straigtforward. I want to be able to measure how slow/fast I'm making my game's graphics before diving in to some hardcore algorithm hacking.

The next nasty problem for me to deal with is the issue of getting the game's controls working across multiple objects. For example, the player's fighter can be composed of a hull, a few guns and several thrusters. Input would need to be affect only relevant objects without causing weird side-effects in the others.

This problem is half solved currently. A single controller object, which is responsible for receiving and processing input from somewhere into actions. Any entity can attach itself to the controller and alter its state based on input from the controller.

A thruster, for example, can decide whether it should turn on based on whether the controller is saying "turn left" or not. This nicely addresses the issue of getting weapons firing in groups, thrusters firing appropriately and so on.

The second half of the problem isn't so easily resolved however. I also have to get state data from the enties that are attached to the controller back to the controller so that they can be sent to the network. My other option is to send complete object state data directly from each entity to/from the network, however that defeats the original purpose of the controller.

An additional problem I didn't quite want to solve this early, but have to, is getting the in-game physics working. This is due to my choice of controlling fighters via believable application of thrust and counter-thrust.

Plenty to mull over. I probably won't have a solution until the end of this week at earliest.