void AcceptConnection() { Socket newSocket; try { if (!listener.Server.IsBound) { return; } newSocket = listener.AcceptSocket(); } catch (Exception e) { /* TODO: Could have an exception here when listener 'goes away' when calling AcceptConnection! */ /* Alternative would be to use locking but the listener doesn't go away without a reason. */ //Log.Write("server", "Accepting the connection failed.", e); return; } var newConn = new ServerConnectoinDefault() { Socket = newSocket }; try { newConn.Socket.Blocking = false; newConn.Socket.NoDelay = true; // assign the player number. newConn.PlayerIndex = ChooseFreePlayerIndex(); SendData(newConn.Socket, BitConverter.GetBytes(ProtocolVersion.Version)); SendData(newConn.Socket, BitConverter.GetBytes(newConn.PlayerIndex)); PreConns.Add(newConn); // Dispatch a handshake order var request = new HandshakeRequest { Mod = ModData.Manifest.Id, Version = ModData.Manifest.Metadata.Version, Map = LobbyInfo.GlobalSettings.Map }; DispatchOrdersToClient(newConn, 0, 0, new ServerOrderDefault("HandshakeRequest", request.Serialize()).Serialize()); } catch (Exception e) { DropClient(newConn); //Log.Write("server", "Dropping client {0} because handshake failed: {1}", newConn.PlayerIndex.ToString(CultureInfo.InvariantCulture), e); } }
public void StartGame() { listener.Stop(); Console.WriteLine("[{0}] Game started", DateTime.Now.ToString(ServerSettings.TimestampFormat)); // Drop any unvalidated clients foreach (var c in PreConns.ToArray()) { DropClient(c); } // Drop any players who are not ready foreach (var c in Conns.Where(c => GetClient(c).IsInvalid).ToArray()) { SendOrderTo(c, "ServerError", "You have been kicked from the server!"); DropClient(c); } // HACK: Turn down the latency if there is only one real player if (LobbyInfo.NonBotClients.Count() == 1) { LobbyInfo.GlobalSettings.OrderLatency = 1; } SyncLobbyInfo(); State = ServerState.GameStarted; foreach (var c in Conns) { foreach (var d in Conns) { DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF }); } } DispatchOrders(null, 0, new ServerOrderDefault("StartGame", "").Serialize()); foreach (var t in serverTraits.WithInterface <IStartGame <ClientDefault, ClientPingDefault> >()) { t.GameStarted(this); } }
public ServerDefault(IPEndPoint endpoint, ServerSettings serverSettings, ModData modData, bool dedicated) { listener = new TcpListener(endpoint); listener.Start(); var localEndpoint = (IPEndPoint)listener.LocalEndpoint; Ip = localEndpoint.Address; Port = localEndpoint.Port; Dedicated = dedicated; ServerSettings = serverSettings; ServerSettings.Name = Engine.Settings.SanitizedServerName(ServerSettings.Name); ModData = modData; randomSeed = (int)DateTime.Now.ToBinary(); // UPnP is only supported for servers created by the game client. //if (!dedicated && ServerSettings.AllowPortForward) // UPnP.ForwardPort(ServerSettings.ListenPort, ServerSettings.ExternalPort).Wait(); foreach (var trait in modData.Manifest.ServerTraits) { serverTraits.Add(modData.ObjectCreator.CreateObject <ServerTrait>(trait)); } LobbyInfo = new Session <ClientDefault, ClientPingDefault> { GlobalSettings = { RandomSeed = randomSeed, Map = serverSettings.Map, ServerName = serverSettings.Name, EnableSingleplayer = serverSettings.EnableSingleplayer || !dedicated, GameUid = Guid.NewGuid().ToString() } }; new Thread(_ => { foreach (var t in serverTraits.WithInterface <INotifyServerStart <ClientDefault, ClientPingDefault> >()) { t.ServerStarted(this); } //Log.Write("server", "Initial mod: {0}", ModData.Manifest.Id); //Log.Write("server", "Initial map: {0}", LobbyInfo.GlobalSettings.Map); var timeout = serverTraits.WithInterface <ITick <ClientDefault, ClientPingDefault> >().Min(t => t.TickTimeout); for (;;) { var checkRead = new List <Socket>(); if (State == ServerState.WaitingPlayers) { checkRead.Add(listener.Server); } checkRead.AddRange(Conns.Select(c => c.Socket)); checkRead.AddRange(PreConns.Select(c => c.Socket)); if (checkRead.Count > 0) { Socket.Select(checkRead, null, null, timeout); } if (State == ServerState.ShuttingDown) { EndGame(); break; } foreach (var s in checkRead) { if (s == listener.Server) { AcceptConnection(); continue; } var preConn = PreConns.SingleOrDefault(c => c.Socket == s); if (preConn != null) { preConn.ReadData(this); continue; } var conn = Conns.SingleOrDefault(c => c.Socket == s); if (conn != null) { conn.ReadData(this); } } foreach (var t in serverTraits.WithInterface <ITick <ClientDefault, ClientPingDefault> >()) { t.Tick(this); } if (State == ServerState.ShuttingDown) { EndGame(); //if (!dedicated && ServerSettings.AllowPortForward) // UPnP.RemovePortForward().Wait(); break; } } foreach (var t in serverTraits.WithInterface <INotifyServerShutdown <ClientDefault, ClientPingDefault> >()) { t.ServerShutdown(this); } PreConns.Clear(); Conns.Clear(); try { listener.Stop(); } catch { } }) { IsBackground = true }.Start(); }
public void DropClient(IServerConnectoin <ClientDefault, ClientPingDefault> toDrop) { if (!PreConns.Remove(toDrop)) { Conns.Remove(toDrop); var dropClient = LobbyInfo.Clients.FirstOrDefault(c1 => c1.Index == toDrop.PlayerIndex); if (dropClient == null) { return; } var suffix = ""; if (State == ServerState.GameStarted) { suffix = dropClient.IsObserver ? " (Spectator)" : ""; } SendMessage("{0}{1} has disconnected.".F(dropClient.Name, suffix)); // Send disconnected order, even if still in the lobby DispatchOrdersToClients(toDrop, 0, new ServerOrderDefault("Disconnected", "").Serialize()); LobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex); LobbyInfo.ClientPings.RemoveAll(p => p.Index == toDrop.PlayerIndex); // Client was the server admin // TODO: Reassign admin for game in progress via an order if (Dedicated && dropClient.IsAdmin && State == ServerState.WaitingPlayers) { // Remove any bots controlled by the admin LobbyInfo.Clients.RemoveAll(c => c.Bot != null && c.BotControllerClientIndex == toDrop.PlayerIndex); var nextAdmin = LobbyInfo.Clients.Where(c1 => c1.Bot == null) .MinByOrDefault(c => c.Index); if (nextAdmin != null) { nextAdmin.IsAdmin = true; SendMessage("{0} is now the admin.".F(nextAdmin.Name)); } } DispatchOrders(toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf }); // All clients have left: clean up if (!Conns.Any()) { foreach (var t in serverTraits.WithInterface <INotifyServerEmpty <ClientDefault, ClientPingDefault> >()) { t.ServerEmpty(this); } } if (Conns.Any() || Dedicated) { SyncLobbyClients(); } if (!Dedicated && dropClient.IsAdmin) { Shutdown(); } } try { toDrop.Socket.Disconnect(false); } catch { } }