Welcome to Lakehome Games

Featured

Lakehome Games is a tiny, but growing, game development studio working on our first commercial release.  We’re slowly becoming more active and recognized in the indie game development community.  Our team is currently composed of 1 designer & illustrator, 1 programmer, 1 musician, 1 3D artist, and 1 manager of mayhem; and we’re looking for people to join us and help us prepare for our first Kickstarter.

But I’ll be honest – sometimes there are sea shanties.

If you’d like the learn more about Lakehome Games, drop us a line:
darrelcusey@lakehomegames.com

Server Migration in Unity 4.0.1

server_migration

Sometimes in game development, it really is best to simply “build your own,” iterating out from core functionality to final product.  This is especially true when you are trying to accommodate a game mechanic that is not served well by existing code bases.  Other times, a game engine or a piece of middleware will do everything you need it to do, and your only job is to simply integrate it correctly.  Unity Networking can be both these things – depending on what you are trying to accomplish.  I have heard the complaints about Unity’s networking model (and probably lodged quite a few myself); but I would also like to show that with a little extra work, you can extend the usefulness of Unity’s built-in networking to a much wider range of projects.

As you probably know by now, I have been writing a series of articles that demonstrate how Unity’s built-in networking can be used for peer-to-peer networking.  In my first article, I demonstrated how a single NetworkView could be used for each client (instead of 1 NetworkView per client and per player).  In my second article, I demonstrated how Server Discovery could be added to this as well.  Building on this foundation, this third article will demonstrate how Server Migration can be achieved.

For those of you unfamiliar with Server Migration… the simplest explanation is that it simply allows your multiplayer game to keep running even when the server has left the game.  One way to accomplish this (and the route we took) is by seamlessly changing the role of a chosen client to the role of server, having all other clients re-connect, and then the game continues.

Server Migration Goals

Server Migration must accomplish two goals:

  1. It must use a server-choosing scheme that will result in the same server being chosen by all clients simultaneously.  That way each client knows whether it will continue to be a client or if its role has changed to the server.
  2. It must guarantee that all clients have (or have access to) the same data that the server is using at any time to maintain game state should the server fail.

If you haven’t grabbed the Unity package yet for this article yet, go grab it from here, open up the NetworkController and let’s walk through some of the more interesting points.

Let’s also use this process flow diagram to make things a little easier to follow:

server_migration_overview

 

Let’s start with a brief overview of what’s happening here.  The Server Migration project is built on top of the Server Discovery project, so the Server Discovery pieces are not shown in the diagram above.  We’ll assume, though, that the client has found a server and successfully connected.  We’ll explain The Happy Path (left-hand side), and The Unhappy Path (right-hand side).

The Happy Path

Most of the time, the client (and server/client) is happily running through its Update() loop.  During that loop, though, we need to check to see if the game state has entered one of three special states: ServerStarting, ServerReconnecting or ServerAdding.

  • ServerStarting happens when the server has disconnected and the local client has determined that it will be the new server.
  • ServerReconnecting happens when the server has disconnected and the local client has determined that it will not be the new server (it will stay in the client role).
  • ServerAdding happens when the client has first connected to a server, and it is waiting a short amount of time before requesting the complete players list.

Each of these states includes a brief delay before they do their work and “back out” from that special state.  Technically speaking, it would be more robust to add an additional conversation between the client and server to let the server control when this transition happens.  I decided to leave those out, however, just to make it easier to see what’s going on — a simple time delay is sufficient for our needs.  Those cases follow this general pattern:

case((int)State.serverstarting):
 if(Time.realtimeSinceStartup > exitStateTime){
 statecontrol.PopState();
 StartServer();
 ListenForClients(statecontrol.GetLastGameName());
 }
 break;

As you can see from the diagram, before “backing out” of these special states, the client performs a different set of actions for each state.  The PopState() function is useful for sending us back to “wherever we were” in the game because the special states can be entered at any time — even during states the represent GUI menu states.  After all, if the server is going to fail, it doesn’t care if you are in the menu or not.

The Unhappy Path

Now that we’ve reviewed the management of these special states — let’s look at the right-hand side of the diagram which shows how those states are entered in the first place.  Before we do that, though, there are two subjects that we must cover…

Synchronicity Fail

From my testing, it appears as though the message dispatcher in Unity is completely independent of the Update() function loop.  In other words, they are asynchronous, or least neither relies upon execution of the other.  Don’t get me wrong, I’m not saying this is a Bad Thing.  However, we must take this into consideration because that means that it is extremely easy to get into a situation where we are generating an OnDisconnectedFromServer() over and over again — with no recourse for an Update() call to get out of it.  This can easily create an infinite loop.  Infinite loops are bad.

The same could be said for almost any built-in Unity message function — there is no guarantee that an Update() will get called in-between any two messages — or even get called at all.  Because of this, we need a way to “put a lock” on the messages so that we can deal with them and then prevent them from being called again until we specifically “unlock” them.  Those checks looks like this:

int cs = statecontrol.GetState();
 if((cs==(int)State.serverstarting) | (cs==(int)State.serverreconnecting)) return;

For this project, I only need this lock for the OnDisconnectedFromServer() message.  When this message is caught by this function, it guarantees that it will always push ServerStarting or ServerReconnecting onto the state stack.  I then check for either of these states at the top of the OnDisconnectedFromServer() function and make and early exit if that is the state we are in.  The Update() function will see these states, and after a short delay, will do the work required to exit those states and finally pop the states off the stack.

The Deal with The NetworkPlayer Struct

I really hope that someone is able to prove me wrong, about this — because it would sure be convenient to use the NetworkPlayer struct (“NPS” for short) to pass-around client data.  All my testing, however, can only lead me to one conclusion — Unity is hard-coded to prevent sending an NPS to a “third party.”  In other words, the client and the server for any given connection can freely pass that client or that server’s NPS back and forth — but one client’s NPS can’t be sent to another client.  Client A can never receive the NPS of client B.

If you think about it, this is a Good Thing because you probably don’t want to expose both your internal (NAT) and external (WAN) IP addresses and ports to a complete stranger (ie. another player in a game that you don’t even know).  I can see how maybe Unity did this on purpose — but it seems odd that they’d go out of their way to do so.  Perhaps RakNet did it for them though :)

In any case, it doesn’t really have that much effect on us — we’ll just send the information we need to send “manually.”  This amounts to little more than adding a couple of additional attributes to the JoinPlayer() signature and makes the code more self-documenting anyways :p

[RPC]
 void JoinPlayer(string ip, int p, string g, Vector3 pos, NetworkViewID nv){
...
}

Back to the Unhappy Path

OK… now that we’ve got those two discussion behind us, all we really need to cover is the function that is used on all clients to come to the same conclusion about who should be the next server.  It’s easy, actually — which just pick the client with the lowest GUID value.  Because all clients know the GUIDs of all other clients, they will all come to the same conclusion.  It also gives us a really good excuse for using LINQ — everyone wins:

chosenServer = players.Aggregate((c, d) => Convert.ToUInt64(c.guid) < Convert.ToUInt64(d.guid) ? c : d);

Returning to The Happy Path

So, there we have it.  When the server disconnects, each client will independently come to the same conclusion about who will be the next server.  The client that has the lowest GUID value will start up a new server and start listening for connections.  The other clients will wait a small delay and then connect to the new server.  The new server will automatically re-associate the connecting clients with their existing game objects at their current locations, and the game continues :)

I hope you enjoyed this article, as always thanks for reading!

 

Unity Networking – What I Really Think

For reasons unknown to me, people will walk right up to me on the street and ask me things like this:

“I’ve heard a lot of people complain about Unity networking.  I’ve been thinking about using a third-party solution.  Even you have brought down some harsh criticism in some of your blog posts.  But, what do you really think about it?”

At that point, I’ll usually just pay my bus fair and look the bus driver straight in the eye and respond “Unity networking is preventing developers from swimming in lava – so, I think it’s a good thing.”  Of course, there’s a lot more to it than that — so, let’s jump right in…

Good Vibrations

It seems to me that the choice of using a simple client-server networking model in Unity was first and foremost nothing more than a good business decision.  I think the vast majority of their customers will only ever need a simple client-server networking model.  Most won’t even need any multiplayer functionality at all (at least the ones who are successful won’t). Also, the vast majority of their customers would be at least somewhat familiar with the basic tenants of the client-server networking model. And with that familiarity comes a sense of ease… which leads to good will, positive word of mouth advertising, greater market share, and ultimately more sales.  I think Unity knew this was the case, so that’s why they kept it simple.

You may have noticed that I kept making customers bold above.  I did that so that you would keep in mind that Unity Technologies is a business — so they must make decisions that will keep their business running.  If you are using the free version of Unity, you should be extremely happy that Unity runs their business this way — it is only because their business model works that you have that free version at all.  

Implementing anything more than a simple client-server networking model (I’ll use MMO networking as an example throughout this post, but the argument holds true for any type of networking) would have been a monumental waste of time and energy.  The number of people that actually could use that technology would be tiny.  The number of developers that actually know how to use that technology AND wouldn’t just want to code it themselves would be infinitesimal.

If Unity had included MMO networking tools, they would have only succeeded in creating an unending stream of complaints from people who were not experienced in multiplayer networking.  The chorus of “it doesn’t work,” would have drowned out everything else — like for example the fact that “you aren’t doing it right.”  It was a good business decision, and that’s why you have a free copy — simple as that.

Judge Not

Let’s just think logically for a minute about who is complaining about Unity networking.  If someone is complaining then that must mean one of the following must be FALSE:

  • The person complaining really MUST actually need that networking functionality for his/her game.
  • The person complaining MUST NOT know how to code that networking functionality themselves.

That may seem a little harsh — but if you think about it for a little bit, you’ll see that these two statements can’t both be true for the same developer.  If someone says to you, “I want to build an MMO – it sucks that Unity doesn’t include MMO networking,” ask them if they have implemented (not just studied) all of the following in C++ (this is a shortened list):

  • Latency Compensation
  • Movement Prediction (Interpolation, Extrapolation)
  • Dead-Reckoning, Splines, or other Message-Reduction techniques
  • Dynamic Server/Zone Partitioning
  • Server Migration
  • Player Migration and Hand-Off
  • Anti-Duping/Cloning code for above Migration functions
  • Active/Passive; Active/Active Servers and Lossless Failover
  • In-Memory Databases and Persistence Priority Considerations

You can replace “MMO” above with “Peer-to-Peer Networking” or any other networking model and change the questions accordingly.  If the answers to any of these questions are “no” then that developer probably isn’t going to be implementing an MMO anytime soon.  I know, still harsh… but keep in mind that most businesses will not consider you an “expert” in any skill until you have at least 10,000 hours under you belt.  If you think about all the foundation knowledge required to just get to the point of programming simple Latency Compensation… well it’s staggering.  It’s amazing that people even get to that point in a single life.  Let’s use an example to elaborate further though…

If you were considering building a house, you probably wouldn’t hire someone who has never used a hammer or any other construction tools before.  You would probably also rank that person’s “house building probability” as near-to-zero.  Also, if you look at a professional carpenter, and you see that she is using a nail gun, you wouldn’t think to yourself, “well, if I just got a nail gun, I could build a house!”  That would be insane.

Just because you have the tool, doesn’t mean you can use it properly.  Using the same line of thinking, even if Unity did include all the tools that you needed to implement MMO networking, how many developers would actually be able to correctly implement them?  And if they did know how to correctly implement them, what are the chances that they would NOT want to just code it themselves?  See what I’m saying here — it’s incredibly unlikely that those two statements are true for the same developer.

When a carpenter uses a nail gun, it’s because she first learned how to use a hammer and has made the informed decision (from years of experience) that the nail gun is the better choice for this particular situation.  When a developer says to me, “well, I’ll just buy a bunch of third-party tools,” in my head I usually imagine a rookie carpenter swinging around a nail gun.  I’m not saying that’s the case for every developer — just the vast majority :)

The Tale of the Real

Another comment I frequently read is that “Unity networking won’t work in a Real World case.”  This to me is the most preposterous statement of all.  What people should be asking is, “Will Unity networking do exactly what its documentation says it will do?” Yes, yes it will.  ”Will Unity networking allow you to run a network game, where the performance of the game and the number of players it can support is directly proportional to the skill that you as a developer have put into its networking and game code?”  Yes, yes it will.

If you swing around a nail gun, you shouldn’t be surprised if people get their eyes shot out.  If you implement Unity networking in your program with very little effort, then you shouldn’t be surprised if you don’t get very good results.  For example, even if we stay strictly in the realm of using only Unity networking (and nothing in C# from .NET), have you considered:

  • Does everything that moves need a Network View?
  • Do Network Views need to be Enabled all the time?
  • Is your object better off with reliable or unreliable Synchronization?
  • Does an object’s Network View really even need to observe its owner as it’s target?
  • Have you considered using a proxy target that has no animation data (or other types of data you don’t actually need)?
  • Then there is the whole area of RPCs — do you need Network Views at all?
  • What kind of information needs to be sent when and to whom?

If you complain about Unity networking, but haven’t even considered any of the above… here, let me show you something… it’s called a hammer :)

So What Do I Really Think?

I think Unity was very smart to include only a basic client-server networking model.  It was  a good business decision, and it kept developers from “swimming in lava” by trying to implement an MMO with no previous experience (which is again good for business).

HOWEVER, I will be the first to admit that Unity networking certainly has its limitations — but those limitations are pretty well documented.  All of its documentation states pretty clearly that it is designed for Client-Server networking.  It even goes as far as to “pull back the covers” on its internal network design (RakNet-based), and even points you to that documentation where you can read all about that middleware’s capabilities.  The Unity community echoes these capabilities on a regular basis as well, so you can’t exactly say how surprised you were when you tried to get 200 clients running off your laptop now can you?

That’s not to say these limitations can’t be overcome with a good understanding of networking and some clever code.  For me, Unity is a challenge — a puzzle box that I can shake and hear all kinds of wonderful things inside.  And every time I unlock a drawer of this proverbial puzzle box called Unity.. every time I find a false bottom or lift the lid of a hidden drawer — I find a little undocumented secret, a cleverly hidden constructor or some odd behavior.  And that’s when something snaps into place, and my understanding of the Why and the How of Unity expands.

And for me… that’s the best game there is :)

 

Server Discovery in Unity 4.0.1

This Unity Package can be downloaded here (updated):

server_discovery_sample_2013_03_07.unitypackage

For most developers, Unity truly goes out of its way to make things as easy as possible for game development.  For example, switching your camera type from perspective to orthographic takes just a click of a button.  Switching lights from spot, to directional, to area; again, just a click of a button.  However, there are a few parts of Unity that, surprisingly, have no options whatsoever.  One of these parts is Unity’s networking design.  Unity is stubbornly Client-Server in its networking design.  There’s no drop-down box where you can switch it over to peer-to-peer networking – this is left as an exercise for the programmer.

A peer-to-peer system, though, can be developed on top of Unity’s existing client server networking framework by adding additional components that will allow the server to migrate among all networked clients.  Minimally, we’ll need to build 3 components: Shared State, Server Discovery and Server Migration.  In my previous article, I described a simple FPS Networking Sample that could maintain state across all clients using a single NetworkView on each client.  An additional advantage to this system is that each client maintained the same information as the “server.”  Because of this, the Shared State requirement has already been met.  The clients are not sharing a lot of information (just the list of players and their locations and targets)… but for now, that’s sufficient.

In this article, I’ll tackle Server Discovery and finally demonstrate Server Migration in a follow-up article.  I’m going to review the most important parts of this solution, so if you haven’t already grabbed the Unity package above, you should do so now so you can follow along.  Let’s start with a process flow diagram…

server_discovery_overview

One of my design goals for this solution was to make it so that a client could easily switch over to become a server at any time.  Until we put in the last Server Migration piece, this is done manually by following these steps:

  1. Start a single-player game
  2. Hit Escape
  3. Select “Open to LAN”

Now your game will be available to others, but most importantly you are able to start a single player game without connecting to a server first.  If you look at a lot of other Unity Networking tutorials, this isn’t even possible.  A lot of other tutorials will use Network.Instantiate(), or else make it so that the client only instantiates a player upon the specific direction of the server.  So, how do we both instantiate at the direction of the server while not actually being connected to a sever?

Easy.  We fake it.  In the diagram above (highlighted in blue), FakeServerJoin() is always executed in any single player game.  This function will start a local server (which won’t actually be used at this point) and then use that local Unity server instance to allocate its own network view.  This is okay to do because (if we become a server later) any clients that connect to us will be drawing network views from the same view ID pool — so there’s no duplication of view IDs.

Also, notice how we create a new NetworkPlayer instance, just by calling its default constructor.  Normally, your NetworkPlayer instance is something that is created and populated “behind the scenes” for OnPlayerConnected().  Because we’re not actually connecting to a server here, OnPlayerConnected() will never get called.  But that’s okay because NetworkPlayer’s default constructor will create “good enough” default values that won’t interfere with any real network players that may connect to us later.  We just allocate a newtwork view, create a default NetworkPlayer instance, send it all along to the JoinPlayer() function, and pretend these aren’t the droids we’re looking for… move along… move along.

At any point now, we can open the game to the LAN and people can connect to us normally.

So, now let’s see what happens when we open the game to the LAN — this is where the real Server Discovery functions come into play.  You may have noticed that Unity doesn’t have a “Network.Broadcast()” function — and why would it?  It strictly adheres to a client-server networking model, after all.  It assumes that the client MUST know the IP address of the server.  So, how do we communicate with a server when we don’t know its IP address (or even if there is a server out there to talk to)?

To accomplish that, the ListenServer() function (highlighted in red) has to do some lower-level networking work.  It has to create its own IPEndPoint, create a UdpClient, and create a UdpState.  These can all be done with just a single statement each — so, it’s not exactly re-inventing the wheel.

However, there are a couple things we need to be careful with.  First and foremost, we must use a random port every time this function is called.  In a given application session, you cannot create 2 UdPClients with the same IP address and same port.  Unity will complain about this and generate a runtime error.  You would see this error on the client if the client connected, dropped, and then tried to rejoin without restarting the application.  The client shouldn’t have to restart the application to re-join, so we have to solve for this limitation.

Update:  I found out that you could actually use 0 for the port number as long as you preset the minPort and maxPort.  This might be a slight improvement as (supposedly) the same port will not be chosen if port 0 is used repeatedly.  If you are feeling adventurous, you may want to try that.

To get around this, we randomize the port on which we’re going to listen for server broadcast responses.  Because the server doesn’t know what port we’re listening on, we have to tell it what that port number is — we do this by sending our listening port number on the server’s known port (which it previously opened for listening for client broadcasts).

Let’s summarize before going further:

  • Server open client listener on Known Port (15000)
  • Client opens a listener on a Random Port (15001 to 15999)
  • Client sends on Known Port to the server its chosen Random Port
  • Server responds to client on the client’s chosen Random Port

Also notice that we are using a lot of asynchronous callbacks.  This is done both in the ListenServer() function and in the ListenForClients() function (highlighted in yellow).  This will (effectively) create a separate thread on which we can react when the conditions for the callback are met.  This is great for a client because it allows the client to still play locally while hosting a game and while the listener thread is independently listening for client join requests :)

It also completely sidesteps the whole “Connect is Blocking” problem (which we’re not even going to talk about here — that would be a whole separate article).  The BeginReceive(new AsyncCallback(ListenServerCallback), us1) function will call the function “ListenServerCallback” when a response is received back on our client’s chosen  Randomly Port number and operate completely on a separate thread.  For the server, the same thing happens with the BeginReceive(new AsyncCallback(ListenForClientsCallback), us1) function, but this time it calls the ListenForClients function when a client sends a message on the Known Port.

Note as well that just opening a listening port won’t (of course) cause a server to respond — so, we have to actually send a message to the server.  This is done with the FindServer() function.  We could send a message to the server something like, “Looking for Server Discovery server,” but that would be a waste.  The server needs to know the chosen Random Port on which we’re listening for responses, so we’ll send that instead.  Notice too that we specifically have to turn on the capability for the UdpClient to broadcast with “uc2.EnableBroadcast = true;” — it’s off by default.

The last point I wanted to discuss here is the need for a Thread.Sleep() function call.  This is needed so that those separate threads that have been started can get a chance to get some processing time.  This must be done any time an asynchronous function is used, so it’s just easiest to put this in the networkController’s Update() function.  The integer it accepts is the number of milliseconds to block the current thread — thereby allowing the listeners some time to check their buffers and execute the callbacks if any messages have been received.

So, that’s pretty much all there is to it.  Adding Server Discovery to Unity.  We’re now two-thirds of the way done to having a full peer-to-peer Unity client.

If you liked this article, please follow me on Twitter @Tulrath to know first when the final part of this series is published.

As always, thanks for reading!

Character Animator Needed

Lakehome Games, LLC is looking for an exceptionally skilled 3D character animator residing in or around the Twin Cities metro area to join our project team. The ideal candidate has experience animating both human and non-human characters, including quadruped animations. A solid grasp of the principles of body movement, follow-through, and other facets of 3D animation is essential. The animator must be well-versed in animation tools — 3DS Max, Maya, etc. The ideal candidate is self-motivated, works well independently and in teams, and has experience working in geographically-dispersed teams.

Additional pluses include:

  • Familiarity with Unity3D and the use of an FBX-based asset pipeline
  • Familiarity with Macanim, re-targetting animations, and blend trees
  • Experience in FX and particle animation
  • Skills in one or more of the related character creation tasks — illustration, modeling, texturing, or rigging
  • A passion for, and experience with, Independent Game Development

This is an unpaid position for a short (~6 month) commercial project with royalty options.

To find out more, please contact us here: darrelcusey@lakehomegames.com

Reflection and Name/Value Pairs

Using Reflection in C# to create a class with a constructor that can set its properties from a delimited name/value pair string.  Just what it says:

public class Creature
{
   public int version { get; private set; }
   public int guid { get; private set; }
   public string name { get; private set; }
   public int health { get; private set; }
   public int status { get; private set; }
   public int skill1 { get; private set; }
   public int skill2 { get; private set; }
   public int skill3 { get; private set; }
   public int skill4 { get; private set; }
   public int ability { get; private set; }
   public int special { get; private set; }
   public int type { get; private set; }
      
   // create a creature using a record string
   public Creature(string str)
   {
      
      char[] delims = new char[]{ ‘|’, ‘,’ };
      char[] assigns = new char[]{ ‘=’ };
      string[] pairs = str.Split(delims);
      Type type = this.GetType();
        PropertyInfo[] properties = type.GetProperties();
 
      foreach(string pair in pairs)
      {
         string pairName = pair.Split(assigns)[0];
         string pairValue  = pair.Split(assigns)[1];
         
         foreach(PropertyInfo propertyInfo in properties)
         {
            if(propertyInfo.Name == pairName)
            {
               this.SetProperty(propertyInfo, pairValue);
            }
         }
         
      }
      
   }
   
   public void SetProperty(PropertyInfo propertyInfo, object value)
    {
        switch(propertyInfo.PropertyType.Name)
        {
            case “Int32″:
                propertyInfo.SetValue(this, Convert.ToInt32(value), null);
                break;
            case “String”:
                propertyInfo.SetValue(this, value.ToString(), null);
                break;
        }
    }
}

…creating an instance of the class with properties set to the values of a name/value pair string is this easy:

Creature c = new Creature(“version=1|guid=1|name=red spider|health=10|skill1=1|skill2=2|type=1″);

World of Creatures “Pet Project”

World of Creatures is a small top-down collecting/battling game that was designed by my 8-year old son. He is a big fan of Pokemon, and wanted to play a game without all the “bad parts” of Pokemon. Here’s just a few of the changes he wanted to make (but the list is extensive!): 1) no forced trainer battles that you can’t run away from or just ignore; 2) you can always see what’s hiding in the tall grass so you can choose whether or not you want to battle/capture it; 3) no city gates that you can’t pass through until you beat the city’s gym leader; 4) gym leaders are always optional; 5) get rid of the “box” system, you can carry all your creatures with you all the time; 6) at the start of a battle, you can choose which creature to use instead of always automatically using the one at the top of the list… and the list goes on and on. This is a “side project” we’ve been working on together for about a month now. Just finished up the design and started coding the core systems.

Unity FPS Networking Sample

fps_sample_screenshot

This Unity Package can be downloaded here (updated):  fps_sample_2013_02_21.unitypackage

Unity is a great tool for Indie developers, but there are parts of it that can really make you spin around in circles.  Networking is definitely one of those parts.  I set out to make a simple peer-to-peer FPS sample project with two goals in mind:

  1. Make it as simple as possible
  2. Avoid (as much as possible) the “exotic” Unity networking components

The components I decided to avoid were NetworkViews, Network.Instantiate, and RemoveRPCs.  I picked these three because NetworkViews (even when set to “Unreliable”) can create a lot of unnecessary network traffic if they are not managed very carefully.  Network.Instantiate also generates so much network traffic that the game can pause and skip every time a new player joins.  And finally, RemoveRPCs (RPC = “Remote Procedure Call”) only works when you are using Network.Instantiate, so it goes out the window as well.  I really wanted to do this as a true peer-to-peer, but Unity’s built-in networking is geared strictly for client-server networking.

Update:  I recently found a good way to do Server Discovery for the LAN, which I should really post soon.

So, what does that leave us with?  It leaves us with…

FPS Sample Using a Single World NetworkView

If you start digging through LOTS of other Unity Networking tutorials, they’ll follow the “standard route” of adding a NetworkView to your player prefab and then relying on Unity to decide when to send updates, and how much data will be sent.  Using this setup, EACH player will have its own chatty NetworkView sending out information to everyone else more or less continuously.

We’re not going to take the standard route.

However, we’re still going to need a single NetworkView because that’s the only way Unity can communicate over the network (since we don’t want to re-create the wheel and build our own Socket manager in .NET).  However, a single NetworkView component attached to an empty GameObject can be set to State Synchronization=OFF, and Observed=NONE.  This will allow us to send RPCs back and forth between client and server without all the overhead and without all the unnecessary complexity.

Which would leave us with something like this:

fps_sample_overview

At the same time, we still want a way to guarantee a unique ID is assigned to each player.  To do this, we could use any of .NET’s functions specifically designed for this task.  But if we did that, then we’d have to import at least another package — which isn’t really worth it just for one function.  A better option is to just use AllocateViewID() to get a unique object on the server even though our actual player object won’t have their own unique network views.  Yes, that’s totally cheating :)

Having just a single NetworkView over which RPCs can be sent means there are a few extra responsibilities we have to take on:

  • Maintaining a dictionary between GameObjects and NetworkPlayer objects so that we can easily lookup one from the other.
  • Managing the creating and destruction of player objects on all clients (from the server) as players join or disconnect.
  • Devising a way for clients to be able to distinguish between messages that are meant for them and messages that are meant for other players

To help explain how this is accomplished, let’s take a look at a process flow diagram.  Did I mention that I <3 process flow diagrams.  I should probably mention that before we get to far into things and PFDs start flying all over the place.

simple_fps

If you haven’t done so already, grab the Unity package and open up the networkController script so you can follow along.

OK, let’s look at the important parts, starting with our first RPC in OnPlayerConnected()

networkView.RPC("JoinPlayer", RPCMode.All, newViewID, Vector3.zero, p);

In OnPlayerConnected(), we execute this RPC call to all connected clients using the server’s world networkView.  Notice that we are using RPCMode.All – which will send the JoinPlayer() RPC to all players and the server.  The player object must be created on all clients and the server, and this is the simplest way of accomplishing that.

Also, notice that the server AND the client is maintaining the HashTable of all players.  This is done in the JoinPlayer() RPC with this simple command:

players.Add(p,newPlayer);

…which is removed when a player disconnects with something very similar in the DisconnectPlayer() RPC:

players.Remove(player);

Now, in the JoinPlayer() RPC, we have this critical comparison:

if(p.ipAddress==LocalAddress)

In Start(), we determine the computer’s Local IP Address using Dns.GetHostEntry(Dns.GetHostName()) from .NET’s System.Net.Socket package and then use this to compare it against the NetworkPlayer structure’s “ipAddress” attribute that is being sent from the server.

Now, at this point, you’re probably asking, “well what about everyone else?”  After all, it’s all well and good that the player and server know about each  other now, but what if there are already players in the game?  How does the server tell the new player about existing players.  Before that can be done, the client must wait until it is fully connected to the server, which is why we wait until the client determines that it is connected before requesting the complete player list from the server.

We use the built-in function OnConnectedToServer() to get our timing just right, and then request the player list:

void OnConnectedToServer()
{ 
   networkView.RPC("SendAllPlayers", RPCMode.Server);
}

Because we use RPCMode.All in the original JoinPlayer() RPC, the server has instantiated a GameObject for every client that is joined.  To get a list of all players, the server only needs to run this code once every time a new player joins in the SendAllPlayers() function:

GameObject[] goPlayers = GameObject.FindGameObjectsWithTag("Player");

For each member of goPlayers, the server extracts the NetworkPlayer and the allocated NetworkViewID, and sends those on using more RPC calls to the JoinPlayer() function.  However, we make sure that we are NOT sending the requestor’s data back to them by doing this comparison:

if(gonp.ToString() != info.sender.ToString())

Because the allocated NetworkViewID is guaranteed unique within the server session, this is far more reliable than using RPCMode.Others (which, despite Unity’s claims to the contrary, actually DOES send a message back to the original sender).

The client ALSO checks the incoming NetworkPlayer data structure’s ipAddress against its own LocalAddress — but this is really just a cross-check to insure that no client can get a doppelganger :)

…because no one like doppelgangers.

So, that’s pretty much it, a complete FPS Networking Sample that only uses a single world NetworkView that generates minimal network traffic.

So is this the end of our peer-to-peer networking dream?  Hmmm, you’ll notice that the Hashtable of all players is being maintained on all clients.  So, in reality, each client already knows everything the server knows.

If you are guessing that I’ve already thought of a way that any given client can:

  • detect when the server disconnects,
  • using ipAddresses, determine if it should take the place of the server,
  • turn itself into the server,
  • and finally tell all the other clients that it is now the server…

…well, if you’ve guessed all that, then you should really consider getting checked out to see if you are psychic or something :D

To see what I’ve got so far, grab the Unity Package here:  fps_sample_2013_02_21.unitypackage

As always, thanks for reading!