Merge pull request #3244 from pchote/bot-surrender

Improvements to player drop behavior
This commit is contained in:
Matthias Mailänder
2013-05-10 01:44:55 -07:00
5 changed files with 86 additions and 81 deletions

View File

@@ -51,14 +51,14 @@ namespace OpenRA.Network
Game.AddChatLine(Color.White, "(player {0})".F(clientId), order.TargetString);
break;
}
case "Message": // Server message
Game.AddChatLine(Color.White, "Server", order.TargetString);
break;
case "Disconnected": /* reports that the target player disconnected */
{
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
if (client != null)
{
client.State = Session.ClientState.Disconnected;
}
break;
}

View File

@@ -306,7 +306,7 @@ namespace OpenRA.Server
t.ClientJoined(this, newConn);
SyncLobbyInfo();
SendChat(newConn, "has joined the game.");
SendMessage("{0} has joined the server.".F(client.Name));
// Send initial ping
SendOrderTo(newConn, "Ping", Environment.TickCount.ToString());
@@ -314,20 +314,18 @@ namespace OpenRA.Server
if (File.Exists("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0])))
{
var motd = System.IO.File.ReadAllText("{0}motd_{1}.txt".F(Platform.SupportDir, lobbyInfo.GlobalSettings.Mods[0]));
SendChatTo(newConn, motd);
SendOrderTo(newConn, "Message", motd);
}
if (lobbyInfo.GlobalSettings.Dedicated)
{
if (client.IsAdmin)
SendChatTo(newConn, " You are admin now!");
else
SendChatTo(newConn, " Current admin is {0}".F(clientAdmin.Name));
var message = client.IsAdmin ? "You are the server admin." : "{0} is the server admin.".F(clientAdmin.Name);
SendOrderTo(newConn, "Message", message);
}
if (mods.Any(m => m.Contains("{DEV_VERSION}")))
SendChat(newConn, "is running a non-versioned development build, "+
"and may cause desync if it contains any incompatible changes.");
SendMessage("{0} is running an unversioned development build, ".F(client.Name) +
"and may desynchronize the game state if they have incompatible rules.");
}
catch (Exception) { DropClient(newConn); }
}
@@ -376,16 +374,19 @@ namespace OpenRA.Server
catch (Exception) { DropClient(c); }
}
public void DispatchOrdersToClients(Connection conn, int frame, byte[] data)
{
var from = conn != null ? conn.PlayerIndex : 0;
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, from, frame, data);
}
public void DispatchOrders(Connection conn, int frame, byte[] data)
{
if (frame == 0 && conn != null)
InterpretServerOrders(conn, data);
else
{
var from = conn != null ? conn.PlayerIndex : 0;
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, from, frame, data);
}
DispatchOrdersToClients(conn, frame, data);
}
void InterpretServerOrders(Connection conn, byte[] data)
@@ -406,32 +407,18 @@ namespace OpenRA.Server
catch (NotImplementedException) { }
}
public void SendChatTo(Connection conn, string text)
{
SendOrderTo(conn, "Chat", text);
}
public void SendOrderTo(Connection conn, string order, string data)
{
DispatchOrdersToClient(conn, 0, 0,
new ServerOrder(order, data).Serialize());
DispatchOrdersToClient(conn, 0, 0, new ServerOrder(order, data).Serialize());
}
public void SendChat(Connection asConn, string text)
public void SendMessage(string text)
{
DispatchOrders(asConn, 0, new ServerOrder("Chat", text).Serialize());
}
public void SendDisconnected(Connection asConn)
{
DispatchOrders(asConn, 0, new ServerOrder("Disconnected", "").Serialize());
DispatchOrdersToClients(null, 0, new ServerOrder("Message", text).Serialize());
}
void InterpretServerOrder(Connection conn, ServerOrder so)
{
var fromClient = GetClient(conn);
var fromIndex = fromClient != null ? fromClient.Index : 0;
switch (so.Name)
{
case "Command":
@@ -443,7 +430,7 @@ namespace OpenRA.Server
if (!handled)
{
Log.Write("server", "Unknown server command: {0}", so.Data);
SendChatTo(conn, "Unknown server command: {0}".F(so.Data));
SendOrderTo(conn, "Message", "Unknown server command: {0}".F(so.Data));
}
break;
@@ -451,16 +438,10 @@ namespace OpenRA.Server
case "HandshakeResponse":
ValidateClient(conn, so.Data);
break;
case "Chat":
case "TeamChat":
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, fromIndex, 0, so.Serialize());
break;
case "PauseGame":
foreach (var c in conns.Except(conn).ToArray())
DispatchOrdersToClient(c, fromIndex, 0, so.Serialize());
DispatchOrdersToClients(conn, 0, so.Serialize());
break;
case "Pong":
{
@@ -471,6 +452,7 @@ namespace OpenRA.Server
break;
}
var fromClient = GetClient(conn);
var history = fromClient.LatencyHistory.ToList();
history.Add(Environment.TickCount - pingSent);
@@ -502,34 +484,37 @@ namespace OpenRA.Server
else
{
conns.Remove(toDrop);
SendChat(toDrop, "Connection Dropped");
OpenRA.Network.Session.Client dropClient = lobbyInfo.Clients.Where(c1 => c1.Index == toDrop.PlayerIndex).Single();
if (State == ServerState.GameStarted)
SendDisconnected(toDrop); /* Report disconnection */
// Send disconnected order, even if still in the lobby
SendMessage("{0} has disconnected.".F(dropClient.Name));
DispatchOrdersToClients(toDrop, 0, new ServerOrder("Disconnected", "").Serialize());
lobbyInfo.Clients.RemoveAll(c => c.Index == toDrop.PlayerIndex);
// reassign admin if necessary
// Client was the server admin
// TODO: Reassign admin for game in progress via an order
if (lobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin && State == ServerState.WaitingPlayers)
{
// clean up the bots that were added by the last admin
// Remove any bots controlled by the admin
lobbyInfo.Clients.RemoveAll(c => c.Bot != null && c.BotControllerClientIndex == toDrop.PlayerIndex);
if (lobbyInfo.Clients.Any(c1 => c1.Bot == null))
OpenRA.Network.Session.Client nextAdmin = lobbyInfo.Clients.Where(c1 => c1.Bot == null)
.OrderBy(c => c.Index).FirstOrDefault();
if (nextAdmin != null)
{
// client was not alone on the server but he was admin: set admin to the last connected client
OpenRA.Network.Session.Client lastClient = lobbyInfo.Clients.Where(c1 => c1.Bot == null).Last();
lastClient.IsAdmin = true;
SendChat(toDrop, "Admin left! {0} is a new admin now!".F(lastClient.Name));
nextAdmin.IsAdmin = true;
SendMessage("{0} is now the admin.".F(nextAdmin.Name));
}
}
DispatchOrders( toDrop, toDrop.MostRecentFrame, new byte[] { 0xbf } );
DispatchOrders(toDrop, toDrop.MostRecentFrame, new byte[] {0xbf});
if (conns.Count != 0 || lobbyInfo.GlobalSettings.Dedicated)
SyncLobbyInfo();
if (!lobbyInfo.GlobalSettings.Dedicated && dropClient.IsAdmin)
Shutdown();
}

View File

@@ -10,6 +10,7 @@
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -31,12 +32,29 @@ namespace OpenRA.Mods.RA
Info = info;
}
Session.Client HumanClient(Player p)
{
var client = p.World.LobbyInfo.ClientWithIndex(p.ClientIndex);
if (client != null && client.Bot != null)
return p.World.LobbyInfo.ClientWithIndex(client.BotControllerClientIndex);
return client;
}
public void Tick(Actor self)
{
if (self.Owner.WinState != WinState.Undefined || self.Owner.NonCombatant) return;
if (self.Owner.NonCombatant)
return;
// Surrender when the controlling player disconnects
var client = HumanClient(self.Owner);
if (client != null && client.State == Session.ClientState.Disconnected)
Lose(self);
if (self.Owner.WinState != WinState.Undefined)
return;
var hasAnything = self.World.ActorsWithTrait<MustBeDestroyed>()
.Any( a => a.Actor.Owner == self.Owner );
.Any(a => a.Actor.Owner == self.Owner);
if (!hasAnything && !self.Owner.NonCombatant)
Lose(self);
@@ -44,9 +62,10 @@ namespace OpenRA.Mods.RA
var others = self.World.Players.Where( p => !p.NonCombatant
&& p != self.Owner && p.Stances[self.Owner] != Stance.Ally );
if (others.Count() == 0) return;
if (others.Count() == 0)
return;
if(others.All(p => p.WinState == WinState.Lost))
if (others.All(p => p.WinState == WinState.Lost))
Win(self);
}

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Mods.RA.Server
if (requiresHost && !client.IsAdmin)
{
server.SendChatTo(conn, "Only the host can do that");
server.SendOrderTo(conn, "Message", "Only the host can do that");
return false;
}
@@ -41,12 +41,12 @@ namespace OpenRA.Mods.RA.Server
{
if (server.State == ServerState.GameStarted)
{
server.SendChatTo(conn, "Cannot change state when game started. ({0})".F(cmd));
server.SendOrderTo(conn, "Message", "Cannot change state when game started. ({0})".F(cmd));
return false;
}
else if (client.State == Session.ClientState.Ready && !(cmd == "ready" || cmd == "startgame"))
{
server.SendChatTo(conn, "Cannot change state when marked as ready.");
server.SendOrderTo(conn, "Message", "Cannot change state when marked as ready.");
return false;
}
@@ -94,7 +94,7 @@ namespace OpenRA.Mods.RA.Server
if (server.lobbyInfo.Slots.Any(sl => sl.Value.Required &&
server.lobbyInfo.ClientInSlot(sl.Key) == null))
{
server.SendChatTo(conn, "Unable to start the game until required slots are full.");
server.SendOrderTo(conn, "Message", "Unable to start the game until required slots are full.");
return true;
}
server.StartGame();
@@ -180,7 +180,7 @@ namespace OpenRA.Mods.RA.Server
if (parts.Length < 3)
{
server.SendChatTo(conn, "Malformed slot_bot command");
server.SendOrderTo(conn, "Message", "Malformed slot_bot command");
return true;
}
@@ -200,7 +200,7 @@ namespace OpenRA.Mods.RA.Server
// Invalid slot
if (bot != null && bot.Bot == null)
{
server.SendChatTo(conn, "Can't add bots to a slot with another client");
server.SendOrderTo(conn, "Message", "Can't add bots to a slot with another client");
return true;
}
@@ -245,12 +245,13 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo( conn, "Only the host can change the map" );
server.SendOrderTo(conn, "Message", "Only the host can change the map");
return true;
}
if(!server.ModData.AvailableMaps.ContainsKey(s))
if (!server.ModData.AvailableMaps.ContainsKey(s))
{
server.SendChatTo( conn, "Map not found");
server.SendOrderTo(conn, "Message", "Map not found");
return true;
}
server.lobbyInfo.GlobalSettings.Map = s;
@@ -292,7 +293,7 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo( conn, "Only the host can set that option" );
server.SendOrderTo(conn, "Message", "Only the host can set that option");
return true;
}
@@ -305,7 +306,7 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo( conn, "Only the host can set that option" );
server.SendOrderTo(conn, "Message", "Only the host can set that option");
return true;
}
@@ -318,14 +319,14 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo(conn, "Only the host can set that option");
server.SendOrderTo(conn, "Message", "Only the host can set that option");
return true;
}
int teams;
if (!int.TryParse(s, out teams))
{
server.SendChatTo(conn, "Number of teams could not be parsed: {0}".F(s));
server.SendOrderTo(conn, "Message", "Number of teams could not be parsed: {0}".F(s));
return true;
}
teams = teams.Clamp(2, 8);
@@ -335,12 +336,12 @@ namespace OpenRA.Mods.RA.Server
.Where(c => c != null && !server.lobbyInfo.Slots[c.Slot].LockTeam).ToArray();
if (players.Length < 2)
{
server.SendChatTo(conn, "Not enough players to assign teams");
server.SendOrderTo(conn, "Message", "Not enough players to assign teams");
return true;
}
if (teams > players.Length)
{
server.SendChatTo(conn, "Too many teams for the number of players");
server.SendOrderTo(conn, "Message", "Too many teams for the number of players");
return true;
}
@@ -367,7 +368,7 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo(conn, "Only the host can set that option");
server.SendOrderTo(conn, "Message", "Only the host can set that option");
return true;
}
@@ -380,13 +381,13 @@ namespace OpenRA.Mods.RA.Server
{
if (!client.IsAdmin)
{
server.SendChatTo(conn, "Only the host can set that option");
server.SendOrderTo(conn, "Message", "Only the host can set that option");
return true;
}
if ((server.Map.Difficulties == null && s != null) || (server.Map.Difficulties != null && !server.Map.Difficulties.Contains(s)))
{
server.SendChatTo(conn, "Unsupported difficulty selected: {0}".F(s));
server.SendChatTo(conn, "Supported difficulties: {0}".F(server.Map.Difficulties.JoinWith(",")));
server.SendOrderTo(conn, "Message", "Unsupported difficulty selected: {0}".F(s));
server.SendOrderTo(conn, "Message", "Supported difficulties: {0}".F(server.Map.Difficulties.JoinWith(",")));
return true;
}
@@ -400,7 +401,7 @@ namespace OpenRA.Mods.RA.Server
if (!client.IsAdmin)
{
server.SendChatTo( conn, "Only the host can kick players" );
server.SendOrderTo(conn, "Message", "Only the host can kick players");
return true;
}
@@ -410,7 +411,7 @@ namespace OpenRA.Mods.RA.Server
var connToKick = server.conns.SingleOrDefault( c => server.GetClient(c) != null && server.GetClient(c).Index == clientID);
if (connToKick == null)
{
server.SendChatTo( conn, "Noone in that slot." );
server.SendOrderTo(conn, "Message", "Noone in that slot.");
return true;
}
@@ -497,7 +498,7 @@ namespace OpenRA.Mods.RA.Server
if (server.lobbyInfo.Clients.Where( cc => cc != client ).Any( cc => (cc.SpawnPoint == spawnPoint) && (cc.SpawnPoint != 0) ))
{
server.SendChatTo( conn, "You can't be at the same spawn point as another player" );
server.SendOrderTo(conn, "Message", "You can't be at the same spawn point as another player");
return true;
}

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Mods.RA.Server
else
lock (masterServerMessages)
while (masterServerMessages.Count > 0)
server.SendChat(null, masterServerMessages.Dequeue());
server.SendMessage(masterServerMessages.Dequeue());
}