Make bots first-class players.

- Bots have their own Clients, with unique ClientIDs
 - Hosts can set bot team/color in the lobby
 - Bots are kicked when switching to a smaller map without enough slots
 - Order validator assumes that only client 0 has permission to issue bot orders
This commit is contained in:
Paul Chote
2011-06-19 02:39:34 +12:00
parent 0c9190a1af
commit 4f172d7ed8
12 changed files with 159 additions and 178 deletions

View File

@@ -34,13 +34,13 @@ namespace OpenRA.Mods.RA
foreach (var kv in w.LobbyInfo.Slots)
{
var client = w.LobbyInfo.ClientInSlot(kv.Key);
if (client == null && kv.Value.Bot == null)
if (client == null)
continue;
var player = new Player(w, client, kv.Value, w.Map.Players[kv.Value.PlayerReference]);
w.AddPlayer(player);
if (client != null && client.Index == Game.LocalClientId)
if (client.Index == Game.LocalClientId)
w.SetLocalPlayer(player.InternalName);
}

View File

@@ -82,8 +82,7 @@ namespace OpenRA.Mods.RA.Server
}
var slot = server.lobbyInfo.Slots[s];
if (slot.Closed || slot.Bot != null ||
server.lobbyInfo.ClientInSlot(s) != null)
if (slot.Closed || server.lobbyInfo.ClientInSlot(s) != null)
return false;
client.Slot = s;
@@ -119,17 +118,20 @@ namespace OpenRA.Mods.RA.Server
var occupant = server.lobbyInfo.ClientInSlot(s);
if (occupant != null)
{
var occupantConn = server.conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index );
if (occupantConn != null)
if (occupant.Bot != null)
server.lobbyInfo.Clients.Remove(occupant);
else
{
server.SendOrderTo(occupantConn, "ServerError", "Your slot was closed by the host");
server.DropClient(occupantConn);
var occupantConn = server.conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index );
if (occupantConn != null)
{
server.SendOrderTo(occupantConn, "ServerError", "Your slot was closed by the host");
server.DropClient(occupantConn);
}
}
}
var slot = server.lobbyInfo.Slots[s];
slot.Closed = true;
slot.Bot = null;
server.lobbyInfo.Slots[s].Closed = true;
server.SyncLobbyInfo();
return true;
}},
@@ -150,7 +152,11 @@ namespace OpenRA.Mods.RA.Server
var slot = server.lobbyInfo.Slots[s];
slot.Closed = false;
slot.Bot = null;
// Slot may have a bot in it
var occupant = server.lobbyInfo.ClientInSlot(s);
if (occupant != null && occupant.Bot != null)
server.lobbyInfo.Clients.Remove(occupant);
server.SyncLobbyInfo();
return true;
@@ -178,10 +184,30 @@ namespace OpenRA.Mods.RA.Server
return true;
}
var botType = string.Join(" ", parts.Skip(1).ToArray() );
var slot = server.lobbyInfo.Slots[parts[0]];
slot.Bot = string.Join(" ", parts.Skip(1).ToArray() );
slot.Closed = false;
var bot = new Session.Client()
{
Index = server.ChooseFreePlayerIndex(),
Name = botType,
Bot = botType,
Slot = parts[0],
Country = "random",
SpawnPoint = 0,
Team = 0,
State = Session.ClientState.NotReady
};
// pick a random color for the bot
var hue = (byte)Game.CosmeticRandom.Next(255);
var sat = (byte)Game.CosmeticRandom.Next(255);
var lum = (byte)Game.CosmeticRandom.Next(51,255);
bot.ColorRamp = new ColorRamp(hue, sat, lum, 10);
S.SyncClientToPlayerReference(client, server.Map.Players[parts[0]]);
server.lobbyInfo.Clients.Add(bot);
server.SyncLobbyInfo();
return true;
}},
@@ -193,20 +219,29 @@ namespace OpenRA.Mods.RA.Server
server.SendChatTo( conn, "Only the host can change the map" );
return true;
}
server.lobbyInfo.GlobalSettings.Map = s;
server.lobbyInfo.GlobalSettings.Map = s;
var oldSlots = server.lobbyInfo.Slots.Keys.ToArray();
LoadMap(server);
// Reassign players into slots
// Reassign players into new slots based on their old slots:
// - Observers remain as observers
// - Players who now lack a slot are made observers
// - Bots who now lack a slot are dropped
var slots = server.lobbyInfo.Slots.Keys.ToArray();
int i = 0;
foreach(var c in server.lobbyInfo.Clients)
foreach (var os in oldSlots)
{
var c = server.lobbyInfo.ClientInSlot(os);
if (c == null)
continue;
c.SpawnPoint = 0;
c.State = Session.ClientState.NotReady;
c.Slot = c.Slot == null || i >= server.lobbyInfo.Slots.Count ?
null : server.lobbyInfo.Slots.ElementAt(i++).Key;
c.Slot = i < slots.Length ? slots[i++] : null;
if (c.Slot != null)
S.SyncClientToPlayerReference(c, server.Map.Players[c.Slot]);
else if (c.Bot != null)
server.lobbyInfo.Clients.Remove(c);
}
server.SyncLobbyInfo();
@@ -282,7 +317,6 @@ namespace OpenRA.Mods.RA.Server
return new Session.Slot
{
PlayerReference = pr.Name,
Bot = null,
Closed = false,
AllowBots = pr.AllowBots,
LockRace = pr.LockRace,

View File

@@ -35,7 +35,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
readonly OrderManager orderManager;
readonly WorldRenderer worldRenderer;
[ObjectCreator.UseCtor]
internal LobbyLogic( [ObjectCreator.Param( "widget" )] Widget lobby, [ObjectCreator.Param] OrderManager orderManager, [ObjectCreator.Param] WorldRenderer worldRenderer)
internal LobbyLogic([ObjectCreator.Param( "widget" )] Widget lobby,
[ObjectCreator.Param] OrderManager orderManager,
[ObjectCreator.Param] WorldRenderer worldRenderer)
{
this.orderManager = orderManager;
this.worldRenderer = worldRenderer;
@@ -93,13 +95,18 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return sc;
};
CountryNames = Rules.Info["world"].Traits.WithInterface<OpenRA.Traits.CountryInfo>().ToDictionary(a => a.Race, a => a.Name);
CountryNames = Rules.Info["world"].Traits.WithInterface<OpenRA.Traits.CountryInfo>()
.ToDictionary(a => a.Race, a => a.Name);
CountryNames.Add("random", "Random");
var mapButton = lobby.GetWidget("CHANGEMAP_BUTTON");
mapButton.OnMouseUp = mi =>
{
Widget.OpenWindow( "MAP_CHOOSER", new WidgetArgs() { { "orderManager", orderManager }, { "mapName", MapUid } } );
Widget.OpenWindow("MAP_CHOOSER", new WidgetArgs()
{
{ "orderManager", orderManager },
{ "mapName", MapUid }
});
return true;
};
@@ -221,11 +228,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
}
}
bool ShowSlotDropDown(DropDownButtonWidget dropdown, Session.Slot slot)
bool ShowSlotDropDown(DropDownButtonWidget dropdown, Session.Slot slot, Session.Client client)
{
var options = new List<SlotDropDownOption>()
{
new SlotDropDownOption("Open", "slot_open "+slot.PlayerReference, () => (!slot.Closed && slot.Bot == null)),
new SlotDropDownOption("Open", "slot_open "+slot.PlayerReference, () => (!slot.Closed && client == null)),
new SlotDropDownOption("Closed", "slot_close "+slot.PlayerReference, () => slot.Closed)
};
@@ -233,7 +240,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
foreach (var b in Rules.Info["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name))
{
var bot = b;
options.Add(new SlotDropDownOption("Bot: {0}".F(bot), "slot_bot {0} {1}".F(slot.PlayerReference, bot), () => slot.Bot == bot));
options.Add(new SlotDropDownOption("Bot: {0}".F(bot),
"slot_bot {0} {1}".F(slot.PlayerReference, bot),
() => client != null && client.Bot == bot));
}
Func<SlotDropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
@@ -327,32 +336,30 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var c = orderManager.LobbyInfo.ClientInSlot(kv.Key);
Widget template;
if (c == null)
if (c == null || c.Bot != null)
{
if (Game.IsHost)
{
template = EmptySlotTemplateHost.Clone();
var name = template.GetWidget<DropDownButtonWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : s.Bot;
name.OnMouseDown = _ => ShowSlotDropDown(name, s);
name.GetText = () => s.Closed ? "Closed" : (c == null) ? "Open" : c.Bot;
name.OnMouseDown = _ => ShowSlotDropDown(name, s, c);
}
else
{
template = EmptySlotTemplate.Clone();
var name = template.GetWidget<LabelWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : s.Bot;
name.GetText = () => s.Closed ? "Closed" : (c == null) ? "Open" : c.Bot;
}
var join = template.GetWidget<ButtonWidget>("JOIN");
if (join != null)
{
join.OnMouseUp = _ => { orderManager.IssueOrder(Order.Command("slot " + s.PlayerReference)); return true; };
join.IsVisible = () => !s.Closed && s.Bot == null && orderManager.LocalClient.State != Session.ClientState.Ready;
join.IsVisible = () => !s.Closed && c == null && orderManager.LocalClient.State != Session.ClientState.Ready;
}
var bot = template.GetWidget<LabelWidget>("BOT");
if (bot != null)
bot.IsVisible = () => s.Bot != null;
template.GetWidget<LabelWidget>("BOT").IsVisible = () => c != null;
}
else if (c.Index == orderManager.LocalClient.Index && c.State != Session.ClientState.Ready)
{
@@ -401,11 +408,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var status = template.GetWidget<CheckboxWidget>("STATUS");
status.IsChecked = () => c.State == Session.ClientState.Ready;
status.OnClick = CycleReady;
var spectator = template.GetWidget<LabelWidget>("SPECTATOR");
Session.Slot ss = s;
spectator.IsVisible = () => ss.Bot != null;
}
else
{
@@ -429,11 +431,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (c.Index == orderManager.LocalClient.Index)
status.OnClick = CycleReady;
var spectator = template.GetWidget<LabelWidget>("SPECTATOR");
Session.Slot ss = s;
spectator.IsVisible = () => ss.Bot != null;
var kickButton = template.GetWidget<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && c.Index != orderManager.LocalClient.Index;
kickButton.OnMouseUp = mi =>