Search Unity

Crab Game (Multiplayer Client/Server w/ Lidgren + Source Code)

Discussion in 'Multiplayer' started by Doddler, Feb 8, 2012.

  1. Doddler

    Doddler

    Joined:
    Jul 12, 2011
    Posts:
    269
    A while ago I finished this game while attempting to teach myself how to program a client/server multiplayer game using Lidgren. The game type wasn't particularly conducive to being made multiplayer, but I think it ended up pretty good! I posted in the showcase section, and while I got some good feedback, the real point I wanted to make was to release code for a multiplayer unity game, and no one touched the multiplayer at all! (horray for scoreboard tracking)

    Anyways, I was thinking maybe it would be better to post here for that. The code is a bit messy, but it has some features that might be interesting to look at. I figure if it should be useful for *someone*, then it's worth it right? It has a game lobby, it syncs and interpolates player positions and actions with each of the clients, and I think does a reasonably well job of making a bullet hell type shooter syncronized across each of the clients without the need to send individual bullet data between clients by using seed values and ensuring enemy actions occur at the same on each PC.

    There's some issues in the game design that limited a perfect network implementation though. Bullets are placed on a 2d plane in line with the player (so that it can collide with them), interpolated based on the position of the emitter and the position of the camera, to give the illusion that the bullet is coming from a specific emitter when in fact it only appears to be doing so from the camera angle. Since the camera itself is based on your own local position and the position of your networked partners though, that means the bullets are appearing in slightly different positions on each machine.

    Well, in the end it's not a huge deal. Unless you're dealing with pings > 200ms, bullets that look like they hit players on your client generally do hit them on theirs. Additionally, by nature of the game, because you aren't shooting each other and you don't necessarily have much opportunity to watch exactly what your teammates are doing, the simulation is more than accurate enough. If I had sync'd camera positions though I probably could have gotten even more accurate though.

    The server itself is a standalone c# project, and does little more than relay messages between players and tell each of the clients what the enemy is currently doing and what point it's aiming at. The server is impressively unaware of the actual game. The server itself could probably be built into the client easily enough, but I wanted to go for a standalone server because of how I planned on structuring my next game.

    More to the point, I'm kind of interested in feedback on the code, if anyone does happen to go through it. I've more or less taught myself this stuff so I imagine my attrocious coding practices could be improved upon. If you have any suggestions for that, I'd love to know!

    Play Game
    Download Full Code
     
  2. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Hi Doddler

    I'll have a quick look when I get a free chance. Thanks for posting.
     
  3. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    I just checked out your site demo of the game in single player mode. Where have I've seen this before ? Did you do this for XNA on Lidgren or something like that on a youtube video ? I think the bullets were a different shape / color.

    Very cool shooter !!! Kudos are in order here. :cool:
     
    Last edited: Feb 11, 2012
  4. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Doddler I haven't had time to go through your code yet but I was able to set up lidgren and play the game on Mac OS Lion or Linux for that matter.

    Some changes need to be made which may help other users who wish to use the Web Player on a Mac/Linux server... Of course you can skip the steps listed below by just switching your Player's Platform Build Settings to Mac Standalone to avoid the Web Player issue altogether...

    Otherwise in Unity to play the Crab Game in the Editor as it comes (Web Player mode) You'll have to change the default Socket Policy Port 843 to something higher like 8843 as unix (Mac) won't allow you to run reserved ports below 1024 unless started by root which is a bad idea anyway.

    On the Client side...
    Edit
    CrabSource/Unity/Assets/Scripts/OpenScene/OpenSceneMgr.cs
    Code (csharp):
    1.  
    2. // About line 49 add the line Security.PrefetchSocketPolicy(....
    3.  if (GUI.Button(new Rect(20, 250, 260, 30), "Play Multi Player"))
    4.         {
    5.             gm.isSoloPlay = false;
    6.             Security.PrefetchSocketPolicy (gm.ipAddress, 8843, 300);
    7.             Application.LoadLevel("mainscene");
    8.         }
    9.  
    On the Server side...
    Edit
    CrabSource/Server/sockpol.cs
    Code (csharp):
    1.  
    2. // About line 74 change the port 843 to the one used from your client (8843)
    3.     public int Start ()
    4.     {
    5.         try {
    6.             listen_socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    7.             listen_socket.Bind (new IPEndPoint (IPAddress.Any, 8843));
    8.             listen_socket.Listen (500);
    9.             listen_socket.Blocking = false;
    10.         }
    11.  
    More information about setting up Unity for http socket served policies can be found here. These are two separate things. One is to get files over http using the WWW class and the other is to allow socket connections to your game server as described below. Note if you want to test your own WWW gets on your own webserver.com/crossdomain.xml from the Unity Editor. Change the URL configured for Unity -> Edit -> Project Settings -> Editor -> WWW Security Emulation to your own web site.

    Running the server in MonoDevelop will require Mono's .Net 4.0 Framework to be implemented instead of the MS Visual Studio references that was used.

    You may get the following error while building the standalone CrabBattleServer:
    Error: Framework '.NETFramework 4.0 Client Profile' not installed. (CrabBattleServer)

    To fix this:
    1. Under your Solution right click the bold "CrabBattleServer" and select -> Options
    2. Select Build -> General -> Change Target framework: .NETFramework 4.0 Client Profile (Not installed) to "Mono / .Net 4.0" in droplist
    3. Click Ok
    Now you should be able to Run the Crab Server on Mac. If you are unable to do so you may have to download and install the latest Mono runtime (2.10.8 stable). This isn't upgrading MonoDevelop directly but you'll notice that MonoDevelop now lists v2.10.8 from the target Debug|x86 drop list.

    Have fun!
     
  5. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Oh God you have an infinite while loop pegging the server's CPU when there is no data!!! I doubt scores could even be posted.

    Program.cs

    Code (csharp):
    1.  
    2.  while(true)
    3.             {
    4.                 //The gamestate stuff comes first because we drop a continue if no packet is received.
    5.                 ...
    6.                 Console.WriteLine("I'm killing your machine !!!");
    7.  
    8.                // IT'S PACKET TIME             
    9.                 if ((inc = server.ReadMessage()) == null) continue;
    10.                
    11.                 switch(inc.MessageType)
    12.                 {
    13.                     case NetIncomingMessageType.ConnectionApproval:
    14.                         //A new connection! If the game is not currently ongoing, we'll accept this connection.
    15.              ....
    16.  
    17.  
    Maybe if you added a delay for the Game loop
    Thread.Sleep (50);

    or waited for input at least...
    server.MessageReceivedEvent.WaitOne();
    if ((inc = server.ReadMessage()) == null) continue;


    But while (true) should be avoided or add a delay and split out the incoming messages in it's own callback...
     
  6. Doddler

    Doddler

    Joined:
    Jul 12, 2011
    Posts:
    269
    Thanks for the feedback. I'm not the best programmer so let me know if you find anything too abhorrent in the code! I kind of just felt my way around until I was able to make something playable, so I'm not really sure if this is best practice. Some kind of formalized RPC call handling might have been more reliable than manually coding handling for each packet type, but for a game of this size it wasn't a big burden.

    The game loop thing didn't even occur to me as being an issue, I only ran it on my desktop and when I was actively testing, with multiple cores it didn't cause any trouble. You're right that it is a rather bad practice though. Good info too on being able to change the policy server port, I didn't see that info before.
     
  7. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Hey thanks for sharing. We're all trying to wrap our heads around multiplayer programming. There's so much to read up on and so little time left for experimentation. The game looks very cool btw.
     
  8. RingOfStorms

    RingOfStorms

    Joined:
    Oct 23, 2010
    Posts:
    584
    Hey downloading this new, I have been trying to make a server similar to this and am about to give up on it. Hopefully your code will help me out. Ill post back after I messed with it a bit.
     
  9. rockysam888

    rockysam888

    Joined:
    Jul 28, 2009
    Posts:
    650
    (bookmarked)
     
  10. kablammyman

    kablammyman

    Joined:
    Nov 22, 2010
    Posts:
    507
    ah, this seems like you updated "ship_test" to add networking. You also gave the full source to that too. What was your genral game plan to sync all the players/bullets together?
     
  11. Doddler

    Doddler

    Joined:
    Jul 12, 2011
    Posts:
    269
    TLDR: For things players do, I would calculate their current position and action based on reported values and lerp it to what the client currently has. For things that are handled by the system (enemy positiion, enemy bullets), an agreed upon future time would be set for when an action would be performed, and seed values would be sent from the server to ensure random values would be the same across each client.

    Original Post:

    Ships, the enemy crab, and bullets are all handled a little differently. For the ships, key presses are sent to the server and then broadcasted to other players. So lets say your friend starts moving left. The command is sent to the server which includes the players position when the command was issued. Your client starts moving that players ship left once you receive the packet. Additionally, your client determines where that players ship SHOULD really be actually be based on the latency of your client to the server and your friends. Your PC will then lerp that position to the players current movement (both real position and current position are updated in the game loop). So basically, if a player changes direction, your client will lerp their current positions and what your client calculated as their REAL position to make a smooth screen position.

    The end result is actually pretty satisfying, the only time the process is even visible is when a player comes to a complete stop, where they will kind of drift back as their current position is farther than their real position. Even then, it was only really obvious when playing with some friends from Australia that had ~300 ping to me. So you get smooth player movement with a pretty reasonable amount of accuracy.

    The crab itself does some fun stuff to keep in sync. Now, players every 1~2 seconds or so send a beat packet, which the server uses to make sure everyone is still connected. I threw in the players position into this packet so the server has a rough idea where everyone is.

    So the crab has one player he picks as his current aim target. When the server receives that beat packet from the targeted player, the server sends a message that basically is "in 500ms, the crab will turn to look at this position", where the position is the reported position of that player. The clients take 500ms and subtract their latency time, and then waits that time, and then (hopefully) everyone at the same time executes the crab's turning action at once.

    The crab's attacks are handled the same way, the server will announce the enemy's intention to use a skill, and also send a seed number to each of the clients. Then at a set time (500ms minus latency) each client will execute the crab's bullet patterns using that seed number. Each bullet pattern is actually just a Coroutine that is started once the command is received. The end result is that the crab should shoot the exact same bullets at the same time in the same directions on each client, without the need to sync each individual bullet. It's not foolproof, but generally each player will see roughly the same thing. In most cases, if you watch a bullet hit another player, that player will report themselves as being hit, which I felt was a satisfying level of synchronicity for a game of this kind.
     
    Last edited: Mar 19, 2012
  12. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    It has begun. I created a VPS the other day and just started a slightly improved CrabBattleServer. I split the main while loop into 2: One is a separate thread checking on the Lobby for Player status (where the continue statement used to be) and the other is a callback waiting on client messages received. The result is a drop on idle CPU usage from 100% to like less than 3%.

    http://www.tomogames.com/CrabBattle/

    I'll update this post with more details but have to run...
     
    Last edited: Mar 25, 2012
  13. Doddler

    Doddler

    Joined:
    Jul 12, 2011
    Posts:
    269
    Nice improvements! I like it. Also I should have added a skip intro option myself. :)

    It would be cool if you could release code for the changes when you're done with it. Not important, but it would be nice to see. :)
     
    Last edited: Mar 26, 2012
  14. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Thanks Doddler. I fixed the score wrapping so player scores don't overlap. I don't have the php scripts though so I used your URL. I suppose its not happy with the cross domain yada or I need a key or something ;)

    Yeah I enabled some of your packet types. If you skip the intro it will uncheck the other clients checkbox. I also added RemovePlayer client side if a player decides to leave the game his ship won't be left to float around. I had to move around some the code blocks to make things work. There is a GameServer class Program only has main. PrefetchSocketPolicy will now lookup your ip address if you use specify a fqdn / hostname. Many other little updates are there...

    My VPS is in Dalles Texas. Ubuntu 9.10 64bit It has 4 cores 2.2ghz and 2GB of ram running mono 2.10.2. I'm curious to see how many users can run at once. The machine was 99% idle with 2 users.

    Edit:
    To Play Online.
    To see the Top Output of my VPS (1 min refresh).
    To see the CrabBattleServer Logs on my VPS.
    To Download Source.

    I'll check back later for updates...
     
    Last edited: Mar 30, 2012
  15. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
  16. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    1. Added usage flag options to change settings
    2. Reduced RunGameLoop delay to 5ms
    3. Improved client server shutdown

    Updated Program.cs GameServer.cs NetworkManager.cs
     
    Last edited: Mar 28, 2012
  17. prophet

    prophet

    Joined:
    Sep 8, 2009
    Posts:
    211
    Thanks for posting this. I will have to check it out when I get home just to see the networking code.
     
  18. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Hi all. I got a few more things working on this. I just updated the live Client and Server on Tomo Games. I'll let you know soon when GitHub gets the update. I just noticed that someone is trying to connect to my server. You will have to update your web client by clearing your browser cache. Otherwise the server will kick you out.

    To note a few new features...
    There is a new KeepAlive system to drop idle clients connections. (Hence why you need the update).
    Players can chat other players while in game.
    Players can join a current game that is already in session.
    Many more changes and tweaks here.
     
  19. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
  20. Tobias J.

    Tobias J.

    Joined:
    Feb 21, 2012
    Posts:
    423
    First, a big thanks to Doddler for making this public, and to tomoprime for picking it up.

    Looks very promising and I thought I'd have it up and running in a matter of hours. But now it's been 2 days as I have a serious compilation problem, and I just can't figure what to do about it.

    The full error I get is:


    Code (csharp):
    1.  
    2. Internal compiler error. See the console log for more information. output was:
    3. Unhandled Exception: System.TypeLoadException: Could not load type 'System.Runtime.Versioning.TargetFrameworkAttribute' from assembly 'Lidgren.Network'.
    4.   at (wrapper managed-to-native) System.Reflection.Assembly:InternalGetType (System.Reflection.Module,string,bool,bool)
    5.   at System.Reflection.Assembly.GetType (System.String name, Boolean throwOnError, Boolean ignoreCase) [0x00000] in <filename unknown>:0
    6.   at System.Reflection.Assembly.GetType (System.String name) [0x00000] in <filename unknown>:0
    7.   at Mono.CSharp.RootNamespace.GetTypeInAssembly (System.Reflection.Assembly assembly, System.String name) [0x00000] in <filename unknown>:0
    8.   at Mono.CSharp.RootNamespace.LookupTypeReflection (Mono.CSharp.CompilerContext ctx, System.String name, Location loc, Boolean must_be_unique) [0x00000] in <filename unknown>:0
    9.   at Mono.CSharp.GlobalRootNamespace.LookupTypeReflection (Mono.CSharp.CompilerContext ctx, System.String name, Location loc, Boolean must_be_unique) [0x00000] in <filename unknown>:0
    10.   at Mono.CSharp.Namespace.LookupType (Mono.CSharp.CompilerContext ctx, System.String name, Location loc) [0x00000] in <filename unknown>:0
    11.   at Mono.CSharp.Namespace.Lookup (Mono.CSharp.CompilerContext ctx, System.String name, Location loc) [0x00000] in <filename unknown>:0
    12.   at Mono.CSharp.TypeManager.CoreLookupType (Mono.CSharp.CompilerContext ctx, System.String ns_name, System.String name, Kind type_kind, Boolean required) [0x00000] in <filename unknown>:0
    13.   at Mono.CSharp.TypeManager.InitCoreTypes (Mono.CSharp.CompilerContext ctx) [0x00000] in <filename unknown>:0
    14.   at Mono.CSharp.Driver.Compile () [0x00000] in <filename unknown>:0
    15.   at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in <filename unknown>:0
    16.  
    The only mention I've been able to find on this errors was here (repeated in a couple of places).

    I've done the few things Judy mentions there, but to no avail.
    I can build the solution with no problems in VS2010, but when I try to run it in the Unity IDE, I get the above message.
    I should mention that the project is a small as it can get. I have nothing other than the lidgren.Network folder in there.


    I'm thinking maybe others had this problem and know how to fix it?

    In any case I'd appreciate any tips or insights on this error.

    -Tobias
     
  21. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    Thanks Tobias.

    From the looks of your error msg:
    Could not load type 'System.Runtime.Versioning.TargetFrameworkAttribute' from assembly 'Lidgren.Network'.

    It would seem you need to switch your Target Framework in MS Studio

    In the above posts I made note of how you would do this change for the Server project under MonoDevelop Going from Windows to Mac.

    You may need to do the opposite.

    But the above is about compiling the server. You mentioned running the client code inside Unity. I just downloaded the CrabBattle zip in GitHub on a windows 7 laptop and didn't open up any source. I only just imported the project into Unity and pressed play in the Editor and the Client was able compile and connect to tomogames.com for multiplayer.

    This is the first time I loaded this build on this machine. I then edited GameManager.cs and changed the greeting message by double clicking the file in Unity to open up the file in MS Visual Studio 2008 Pro. I suppose this isn't VS2010 but I would think the Unity Editor would generate the solution files on the fly.

    Tobias try deleting or moving your VS files in the Unity project folder so that Unity can regenerate these upon reopening project. Perhaps I will omit these files from GitHub as every clients settings vary with each machine.

    EDIT:
    Let me also note that Unity is going to compile the Client code not MS VS or MonoDevelop which is contributing to your problem...
     
    Last edited: May 2, 2012
  22. Tobias J.

    Tobias J.

    Joined:
    Feb 21, 2012
    Posts:
    423
    I'm almost too embarrassed to post about it, but I guess that's the least I can do.

    Thanks for trying to figure some way out of this, tomoprime. I'm afraid the reason was primarily my own laziness. I have had a unity warning about a missing fix for x64 systems (http://support.microsoft.com/kb/976038), but the resolution section just seemed like a big hassle when I didn't have any problems at all. Not sure if the hotfix download was available when I read it the first time, but I didn't see it, thinking I would have to follow the steps under 'resolution'.

    In any case, I figured I'd better eliminate all other sources of error, and so went back to go through the fix - and then saw the download. Voila, and the code compiles in unity as well. :)

    While I'm just happy as can be right now, I'm sorry and ashamed to have wasted your time because I ignored a warning.

    Apologies and gratitude at ya!
     
  23. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    No worries Tobias. I am glad it all worked out. I'll be logged into to the game on my server if you want to test it out for a little while. The US server is in Texas and we can expect some latency being in your neck of the woods. :D
     
  24. Tomo-Games

    Tomo-Games

    Joined:
    Sep 20, 2010
    Posts:
    223
    New update on GitHub...

    Current Build v1.2

    • Multiplayer Added support for changing player name while in game. GUIText will get updated on other clients. Added Sequence Channel numbering grouped by message / packet types see GameServer.cs and NetworkManager.cs for additional notes.
    • Fixed misc bugs. Clean up some warning messages.
     
  25. Tobias J.

    Tobias J.

    Joined:
    Feb 21, 2012
    Posts:
    423
    Just wanted to return and let you guys know that I never actually got this to work. Has anyone managed to set this up on 64-bit systems?

    The lidgren library still refused to compile, and though I think I found solutions for it, Unity kept overwriting the project values I set in VS2010.
    This overruling of project settings in VS has kept tripping me up, most recently Unity has begun removing references and re-setting the target .NET version (which has made it a nightmare to set up a direct SQL connection).

    As I can't set up things like Photon and uLink on my host, I'm back to going through the website via WWW calls. It's not that bad for a turn-based game, but I find the networking capabilities in Unity very hard to navigate.

    Anyways, sorry for the rant. Keep up the good work! :)
     
  26. Shedletsky

    Shedletsky

    Joined:
    Apr 21, 2014
    Posts:
    20
    This demo is very helpful, thanks so much for making it available.

    I was especially impressed by how easy it was to build in Visual Studio.