Original Post
Hi all, I hope you can help me with this. I started to develop a small formula one management simulation a few months ago and its by now I'd say 70 % complete. But I still got problems with my multiplayer mode. I wanted to implement a multiplayer mode for up to ten players (10 teams in F1) where one player is the host and the others connect as clients. The game has a main form and various user controls where each user control stands for a certain view in the game. For example the start screen is one view, the manager's office is one view etc. Additionally I got a class OvServer, a class OvClient and a class OvRemoteObject and the RemoteObject has an instance of the class OvGameState. Now the player who hosts a game will have an OvServer instance created in his main form and all players, including the host, have an OvClient instance created in the Main Form. The server enables the remote-object instance and all clients communicate with that remote object. So far, so good. It all works without any problems as long as I use it within a local area network, but as soon as I try to connect via the internet there are some problems. It seems like the client can still communicate with the server but the server can't call any clients anymore. For example when a new client connects to the server, the host displays the new player's name and stats in the lobby. This is still working over the internet. But all other clients should get information about the new client as well but it doesn't update the other clients' lobby screen. The server can't call any functions in the client and it can't raise any events. On client side it just seems like there is no server at all, but the server is getting all information from the clients correctly. Funny thing is, it doesn't rise any exceptions or something (like security exception or something), neither on client nor on server side. It just calls the functions as usual (I can see that by debugging) but they are not being executed on the client side. Does anybody know what this could be about ? I already thought about letting the client poll the server using a timer to get the information it needs from the server but I don't have any experience about how this would influence the runtime / traffic and if it still would be playable then. Additionally I'm quite happy about my current design because its quite modular and I would like to reuse the server/client classes in further projects. So changing my design would only be an option if anything else fails. I hope anybody can help me. Here some code for the better understanding : (I marked the problematic lines with an ***) server-initialisation : OvServer : OvClient : OvRemoteObject :
///
// Method used to open the server connection (when player is host)
///
private void btn_Go_Click(object sender, System.EventArgs e)
{
//start the server
OvServer.StartConnection();
//start the client
//if the player is hosting, server and client are on the same machine
//so we can use localhost as url
OvClient.TryConnection(portNo, "localhost");
// subscribe to the event which is fired when a new client connects
Overlapped.ClientConnected += new EmptyDelegate(OnClientConnected);
// host always has the Player-ID 1
mMainForm.PlayerInstance.PlayerID = 1;
// tell the server that a client connected (host instance itself connects)
OvClient.Server.ClientConnected(mMainForm.PlayerInstance);
// Init refresh timer to check for updates
mRefreshTimer.AutoReset = true;
mRefreshTimer.Start();
mRefreshTimer.Elapsed += new System.Timers.ElapsedEventHandler(mRefreshTimer_Elapsed);
// subsribe to the event that is triggered if any of the clients changed
// its team selection
OvClient.Server.ConnectionStatusChanged +=new EmptyDelegate(Server_ConnectionStatusChanged);
// subscribe to the event that is triggered if any of the clients changed
// its ready-state
OvClient.Server.ReadyStateChanged += new EmptyDelegate(Server_ReadyStateChanged);
// show the host user that connection has been opened
this.lbl_ConnectionStatus.Text = "Waiting for Players";
this.lbl_ConnectionStatus.BackColor = Color.YellowGreen;
}
using System;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using Overlapped.IniFile;
namespace Overlapped.Server
{
/// <summary>
/// The game server, singleton class
/// </summary>
public class OvServer
{
private static int mPortNo;
private OvIniHandler mIniFile;
private static OvServer mInstance = new OvServer();
private OvServer()
{
mIniFile = new OvIniHandler(Application.StartupPath + @"\ServerSettings.ini");
mPortNo = mIniFile.GetInt("Connection", "ServerPort", 8050);
}
// Starting the server connection / enabling the remote object
public static void StartConnection()
{
// create my own sinks because type filter level has to be set to full
BinaryClientFormatterSinkProvider clientProvider = new BinaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
// Set TypeFilterLevel to full (needed so send remote events)
serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
// Initialize the server channel
IDictionary props = new Hashtable();
props["port"] = mPortNo;
props["typeFilterLevel"] = TypeFilterLevel.Full;
HttpChannel chan = new HttpChannel(props,clientProvider,serverProvider);
// Register server channel
ChannelServices.RegisterChannel(chan);
//enable remote object
RemotingConfiguration.RegisterWellKnownServiceType(typeof(OvRemoteObject),"Overlapped",WellKnownObjectMode.Singleton);
}
// get singleton-instance
public static OvServer GetInstance()
{
return mInstance;
}
}
}
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.Runtime.Serialization.Formatters;
using Overlapped.Server;
namespace Overlapped.Client
{
/// <summary>
/// Client Class
/// </summary>
public class OvClient
{
/// <summary>
/// Singleton instance of this client
/// </summary>
private static OvClient mInstance = new OvClient();
private static OvRemoteObject mServer;
private OvClient()
{
mServer = null;
}
public static OvClient GetInstance()
{
return mInstance;
}
//Connect to the server
public static void TryConnection(int port, string url)
{
BinaryClientFormatterSinkProvider clientProvider = new inaryClientFormatterSinkProvider();
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
//init client channel
IDictionary props = new Hashtable();
props["port"] = 0;
//create unique client name
string s = System.Guid.NewGuid().ToString();
props["name"] = s;
props["typeFilterLevel"] = TypeFilterLevel.Full;
HttpChannel chan = new HttpChannel(props,clientProvider,serverProvider);
ChannelServices.RegisterChannel(chan);
Type typeofRO = typeof(OvRemoteObject);
//Activate RemoteObject
mServer = (OvRemoteObject)Activator.GetObject(typeofRO, "http://"+url+":"+port+"/Overlapped");
}
public static OvRemoteObject Server
{
get {return mServer;}
}
public static Hashtable GetPlayerConnections()
{
return mServer.GetPlayerSelections();
}
}
}
using System;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.Remoting;
using Overlapped.TypeDefs;
using Overlapped.Core;
namespace Overlapped.Server
{
[Serializable]
public class OvRemoteObject : MarshalByRefObject
{
private Hashtable mPlayerConnectionStatus;
private OvGameState mGameState;
public OvRemoteObject()
{
mPlayerConnectionStatus = new Hashtable();
mGameState = new OvGameState();
}
public static event InitDelegate NewClient;
public event EmptyDelegate ConnectionStatusChanged;
public event EmptyDelegate ReadyStateChanged;
// a new client connected to the server
public int ClientConnected(OvPlayer playerInstance)
{
// assign player-ID to the new player instance
playerInstance.PlayerID = mPlayerConnectionStatus.Count + 1;
// save player instance
mPlayerConnectionStatus.Add(playerInstance.PlayerID, playerInstance);
// notify all other clients that a new client connected
// (exclude server, because it has been notified already)
if(playerInstance.PlayerName
!=((OvPlayer)mPlayerConnectionStatus[1]).PlayerName)
*** // use method in the client instance for notification (only works in LAN)
*** playerInstance.NewClient(playerInstance);
*** // use event to notify clients (only works in LAN)
*** //NewClient(playerInstance);
*** // set new connection status in all clients (only works in LAN)
*** if(ConnectionStatusChanged != null)
*** ConnectionStatusChanged();
// return last Player-ID
return mPlayerConnectionStatus.Count;
}
//works
public Hashtable GetPlayerConnectionStatus()
{
return (Hashtable)(mPlayerConnectionStatus.Clone());
}
//works
public Hashtable GetPlayerSelections()
{
return mPlayerConnectionStatus;
}
public OvGameState GameState
{
get { return mGameState; }
set { mGameState = value; }
}
// Notify clients if connection state changed
public void UpdateConnectionStatus(int playerNo, string playerSelection)
{
((OvPlayer)mPlayerConnectionStatus[playerNo]).Team = playerSelection;
*** if(ConnectionStatusChanged != null)
*** ConnectionStatusChanged(); // Only works via LAN
}
// Notify clients if ready-state changed
public void UpdateReadyStatus(int playerNo, bool readyState)
{
((OvPlayer)mPlayerConnectionStatus[playerNo]).ReadyState = readyState;
*** if(ReadyStateChanged != null)
*** ReadyStateChanged(); // Only works via LAN
}
public Hashtable PlayerConnectionStatus
{
get { return mPlayerConnectionStatus; }
set { mPlayerConnectionStatus = value; }
}
public void AddPlayerToGameState(int pID, OvPlayer player)
{
GameState.AddPlayer(pID, player);
}
// ... (cut off the rest)
// ...
}
}