diff --git a/OpenRA.Game/Network/Handshake.cs b/OpenRA.Game/Network/Handshake.cs new file mode 100644 index 0000000000..6cebf19a9e --- /dev/null +++ b/OpenRA.Game/Network/Handshake.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007-2010 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see LICENSE. + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Network +{ + public class HandshakeResponse + { + public string Name; + public Color Color1; + public Color Color2; + public string[] Mods = { "ra" }; // mod names + public string Password; + + public string Serialize() + { + var data = new List(); + data.Add(new MiniYamlNode("Handshake", FieldSaver.Save(this))); + System.Console.WriteLine("Serializing handshake response:"); + System.Console.WriteLine(data.WriteToString()); + + return data.WriteToString(); + } + + public static HandshakeResponse Deserialize(string data) + { + System.Console.WriteLine("Deserializing handshake response:"); + System.Console.WriteLine(data); + + var handshake = new HandshakeResponse(); + var ys = MiniYaml.FromString(data); + FieldLoader.Load(handshake, ys.First().Value); + return handshake; + } + } +} \ No newline at end of file diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index 300d718a96..d0900d618e 100755 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -195,6 +195,11 @@ namespace OpenRA return new Order("TeamChat", null, false) { IsImmediate = true, TargetString = text }; } + public static Order HandshakeResponse(string text) + { + return new Order("HandshakeResponse", null, false) { IsImmediate = true, TargetString = text }; + } + public static Order Command(string text) { return new Order("Command", null, false) { IsImmediate = true, TargetString = text }; diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index d3aed7a308..23b42fb0bd 100755 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -11,6 +11,7 @@ using System.Drawing; using System.Linq; using OpenRA.Traits; +using System; namespace OpenRA.Network { @@ -96,8 +97,37 @@ namespace OpenRA.Network Game.StartGame(orderManager.LobbyInfo.GlobalSettings.Map); break; } + + case "HandshakeRequest": + { + Console.WriteLine("Client: Recieved HandshakeRequest"); + // Check valid mods/versions + var serverInfo = Session.Deserialize(order.TargetString); + var serverMods = serverInfo.GlobalSettings.Mods; + var localMods = orderManager.LobbyInfo.GlobalSettings.Mods; + + // TODO: Check that the map exists on the client + + // Todo: Display a friendly dialog + if (serverMods.SymmetricDifference(localMods).Count() > 0) + throw new InvalidOperationException("Version mismatch. Client: `{0}`, Server: `{1}`" + .F(string.Join(",",localMods), string.Join(",",serverMods))); + + var response = new HandshakeResponse() + { + Name = "Test Player", + Color1 = Color.PaleGreen, + Color2 = Color.PeachPuff, + Mods = localMods, + Password = "Foo" + }; + orderManager.IssueOrder(Order.HandshakeResponse(response.Serialize())); + break; + } + case "SyncInfo": { + Console.WriteLine("Client: Recieved SyncInfo"); orderManager.LobbyInfo = Session.Deserialize(order.TargetString); if (orderManager.FramesAhead != orderManager.LobbyInfo.GlobalSettings.OrderLatency diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 3ba6a6d040..585a3598ee 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -1,4 +1,4 @@ - + Debug @@ -180,6 +180,7 @@ + diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index f08ff0497f..5ad6f37c0b 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -24,7 +24,12 @@ namespace OpenRA.Server { public class Server { + // Valid player connections public List conns = new List(); + + // Pre-verified player connections + public List preConns = new List(); + TcpListener listener = null; Dictionary> inFlightFrames = new Dictionary>(); @@ -88,24 +93,31 @@ namespace OpenRA.Server var checkRead = new ArrayList(); checkRead.Add( listener.Server ); foreach( var c in conns ) checkRead.Add( c.socket ); - + foreach( var c in preConns ) checkRead.Add( c.socket ); + Socket.Select( checkRead, null, null, timeout ); foreach( Socket s in checkRead ) if( s == listener.Server ) AcceptConnection(); + else if (preConns.Count > 0) + { + var p = preConns.SingleOrDefault( c => c.socket == s ); + if (p != null) p.ReadData( this ); + } else if (conns.Count > 0) conns.Single( c => c.socket == s ).ReadData( this ); foreach (var t in ServerTraits.WithInterface()) t.Tick(this); - if (conns.Count() == 0 || shutdown) + if (shutdown) break; } GameStarted = false; foreach (var t in ServerTraits.WithInterface()) t.ServerShutdown(this); - + + preConns.Clear(); conns.Clear(); try { listener.Stop(); } catch { } @@ -120,7 +132,7 @@ namespace OpenRA.Server int ChooseFreePlayerIndex() { for (var i = 0; i < 256; i++) - if (conns.All(c => c.PlayerIndex != i)) + if (conns.All(c => c.PlayerIndex != i) && preConns.All(c => c.PlayerIndex != i)) return i; throw new InvalidOperationException("Already got 256 players"); @@ -134,7 +146,8 @@ namespace OpenRA.Server { if (!listener.Server.IsBound) return; newSocket = listener.AcceptSocket(); - }catch + } + catch { /* could have an exception here when listener 'goes away' when calling AcceptConnection! */ /* alternative would be to use locking but the listener doesnt go away without a reason */ @@ -142,6 +155,26 @@ namespace OpenRA.Server } var newConn = new Connection { socket = newSocket }; + try + { + newConn.socket.Blocking = false; + newConn.socket.NoDelay = true; + + // assign the player number. + newConn.PlayerIndex = ChooseFreePlayerIndex(); + newConn.socket.Send(BitConverter.GetBytes(ProtocolVersion.Version)); + newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex)); + preConns.Add(newConn); + + // Dispatch a handshake order + DispatchOrdersToClient(newConn, 0, 0, new ServerOrder("HandshakeRequest", lobbyInfo.Serialize()).Serialize()); + } + catch (Exception) { DropClient(newConn); } + } + + void AcceptPlayer(Connection newConn) + { + try { if (GameStarted) @@ -152,17 +185,13 @@ namespace OpenRA.Server return; } - newConn.socket.Blocking = false; - newConn.socket.NoDelay = true; - - // assign the player number. - newConn.PlayerIndex = ChooseFreePlayerIndex(); - newConn.socket.Send(BitConverter.GetBytes(ProtocolVersion.Version)); - newConn.socket.Send(BitConverter.GetBytes(newConn.PlayerIndex)); + preConns.Remove(newConn); conns.Add(newConn); foreach (var t in ServerTraits.WithInterface()) t.ClientJoined(this, newConn); + + Console.WriteLine("Server: Accepted connection as player"); } catch (Exception) { DropClient(newConn); } } @@ -246,20 +275,29 @@ namespace OpenRA.Server switch (so.Name) { case "Command": + bool handled = false; + foreach (var t in ServerTraits.WithInterface()) + if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) + break; + + if (!handled) { - bool handled = false; - foreach (var t in ServerTraits.WithInterface()) - if ((handled = t.InterpretCommand(this, conn, GetClient(conn), so.Data))) - break; - - if (!handled) - { - Log.Write("server", "Unknown server command: {0}", so.Data); - SendChatTo(conn, "Unknown server command: {0}".F(so.Data)); - } + Log.Write("server", "Unknown server command: {0}", so.Data); + SendChatTo(conn, "Unknown server command: {0}".F(so.Data)); } + + break; + case "HandshakeResponse": + Console.WriteLine("Server Recieved Handshake response"); + var response = HandshakeResponse.Deserialize(so.Data); + + // Validate versions again + + // Validate password + + // Accept connection; set name, color, etc. + AcceptPlayer(conn); break; - case "Chat": case "TeamChat": var fromClient = GetClient(conn);