1
0
forked from mirrors/0ad

Further gamesetup fixes & tweaks.

Fixes dc18d94030

- Player assignment still had some trouble. This cleans things up by
moving a little more logic in the controller & fixing other usage.
 - As a consequence, in MP, if the host swaps two player, the change
will take a few frames to register. It shouldn't be too much of a
problem in practice.
 - Fix issues with AI being assigned to player slots for joiners.
- Fix issues with similar color not being correctly picked when swapping
a map.
- Fix issues with rating & locked team settings.

Reported by: langbart
Differential Revision: https://code.wildfiregames.com/D3723
This was SVN commit r25096.
This commit is contained in:
wraitii
2021-03-21 13:09:52 +00:00
parent 841bf5f2dd
commit fa3b341b57
5 changed files with 99 additions and 69 deletions
@@ -59,12 +59,10 @@ GameSettings.prototype.Attributes.PlayerColor = class PlayerColor extends GameSe
// Reset.
this.locked = this.locked.map(x => this.settings.map.type == "scenario");
this.trigger("locked");
if (this.settings.map.type === "scenario")
this._resize(0);
this._updateAvailable();
this.maybeUpdate();
this.maybeUpdate();
}
maybeUpdate()
@@ -80,20 +78,12 @@ GameSettings.prototype.Attributes.PlayerColor = class PlayerColor extends GameSe
let inUse = this.values.findIndex((otherColor, i) =>
color && otherColor &&
sameColor(color, otherColor));
if (inUse !== -1 && inUse !== playerIndex)
if (inUse != -1 && inUse != playerIndex)
{
if (sameColor(this.values[playerIndex], this.values[inUse]))
{
this.values[playerIndex] = undefined;
color = undefined;
}
else
{
// Swap colors.
let col = this.values[playerIndex];
this.values[playerIndex] = undefined;
this._set(inUse, col);
}
// Swap colors.
let col = this.values[playerIndex];
this.values[playerIndex] = undefined;
this._set(inUse, col);
}
if (!color || this.available.indexOf(color) == -1)
{
@@ -140,20 +130,34 @@ GameSettings.prototype.Attributes.PlayerColor = class PlayerColor extends GameSe
// Pick colors that the map specifies, add most unsimilar default colors
// Provide the access to g_MaxPlayers different colors, regardless of current playercount.
let values = [];
let mapColors = false;
for (let i = 0; i < g_MaxPlayers; ++i)
values.push(this._getMapData(i) ||
this.defaultColors[i] || this._findFarthestUnusedColor(values));
{
let col = this._getMapData(i);
if (col)
mapColors = true;
if (mapColors)
values.push(col || this._findFarthestUnusedColor(values));
else
values.push(this.defaultColors[i]);
}
this.available = values;
}
_findClosestColor(targetColor, colors)
{
let colorDistances = colors.map(color => colorDistance(color, targetColor));
let smallestDistance = colorDistances.find(
distance => colorDistances.every(distance2 => distance2 >= distance));
return colors.find(color => colorDistance(color, targetColor) == smallestDistance);
let closestColor;
let closestColorDistance = 0;
for (let color of colors)
{
let dist = colorDistance(targetColor, color);
if (!closestColor || dist < closestColorDistance)
{
closestColor = color;
closestColorDistance = dist;
}
}
return closestColor;
}
_findFarthestUnusedColor(values)
@@ -15,7 +15,7 @@ GameSettings.prototype.Attributes.Rating = class Rating extends GameSetting
fromInitAttributes(attribs)
{
if (this.getLegacySetting(attribs, "RatingEnabled"))
if (this.getLegacySetting(attribs, "RatingEnabled") !== undefined)
{
this.available = this.hasXmppClient && this.settings.playerCount.nbPlayers === 2;
this.enabled = this.available && !!this.getLegacySetting(attribs, "RatingEnabled");
@@ -17,10 +17,11 @@ class PlayerAssignmentsControl
// Replace empty player name when entering a single-player match for the first time.
Engine.ConfigDB_CreateAndWriteValueToFile("user", this.ConfigNameSingleplayer, name, "config/user.cfg");
// By default, assign the player to the first slot.
g_PlayerAssignments = {
"local": {
"name": name,
"player": -1
"player": 1
}
};
}
@@ -30,6 +31,8 @@ class PlayerAssignmentsControl
setupWindow.registerLoadHandler(this.onLoad.bind(this));
setupWindow.registerGetHotloadDataHandler(this.onGetHotloadData.bind(this));
netMessages.registerNetMessageHandler("players", this.onPlayerAssignmentMessage.bind(this));
this.registerClientJoinHandler(this.onClientJoin.bind(this));
}
registerPlayerAssignmentsChangeHandler(handler)
@@ -69,6 +72,13 @@ class PlayerAssignmentsControl
g_PlayerAssignments = hotloadData.playerAssignments;
this.updatePlayerAssignments();
}
else if (!g_IsNetworked)
{
// Simulate a net message for the local player to keep a common path.
this.onPlayerAssignmentMessage({
"newAssignments": g_PlayerAssignments
});
}
}
onGetHotloadData(object)
@@ -76,6 +86,38 @@ class PlayerAssignmentsControl
object.playerAssignments = g_PlayerAssignments;
}
/**
* On client join, try to assign them to a free slot.
* (This is called before g_PlayerAssignments is updated).
*/
onClientJoin(newGUID, newAssignments)
{
if (!g_IsController || newAssignments[newGUID].player != -1)
return;
// Assign the client (or only buddies if prefered) to a free slot
if (newGUID != Engine.GetPlayerGUID())
{
let assignOption = Engine.ConfigDB_GetValue("user", this.ConfigAssignPlayers);
if (assignOption == "disabled" ||
assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(newAssignments[newGUID].name).nick) == -1)
return;
}
// Find a player slot that no other player is assigned to.
let firstFreeSlot = [...Array(g_MaxPlayers).keys()];
firstFreeSlot = firstFreeSlot.find(i => {
for (let guid in newAssignments)
if (newAssignments[guid].player == i + 1)
return false;
return true;
});
if (firstFreeSlot === -1)
return;
this.assignClient(newGUID, firstFreeSlot + 1);
}
/**
* To be called when g_PlayerAssignments is modified.
*/
@@ -112,12 +154,13 @@ class PlayerAssignmentsControl
assignClient(guid, playerIndex)
{
g_GameSettings.playerAI.setAI(playerIndex - 1, undefined);
if (g_IsNetworked)
Engine.AssignNetworkPlayer(playerIndex, guid);
if (g_PlayerAssignments[guid])
else
{
g_PlayerAssignments[guid].player = playerIndex;
this.updatePlayerAssignments();
this.updatePlayerAssignments();
}
}
/**
@@ -151,9 +194,11 @@ class PlayerAssignmentsControl
unassignInvalidPlayers()
{
if (g_IsNetworked)
for (let playerID = g_GameSettings.playerCount.nbPlayers + 1; playerID <= g_MaxPlayers; ++playerID)
// Remove obsolete playerIDs from the servers playerassignments copy
Engine.AssignNetworkPlayer(playerID, "");
{
for (let guid in g_PlayerAssignments)
if (g_PlayerAssignments[guid].player > g_GameSettings.playerCount.nbPlayers)
Engine.AssignNetworkPlayer(g_PlayerAssignments[guid].player, "");
}
else if (g_PlayerAssignments.local.player > g_GameSettings.playerCount.nbPlayers)
{
g_PlayerAssignments.local.player = -1;
@@ -164,3 +209,6 @@ class PlayerAssignmentsControl
PlayerAssignmentsControl.prototype.ConfigNameSingleplayer =
"playername.singleplayer";
PlayerAssignmentsControl.prototype.ConfigAssignPlayers =
"gui.gamesetup.assignplayers";
@@ -27,12 +27,11 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
this.assignedGUID = undefined;
this.fixedAI = undefined;
// Build the initial list of values with undefined & AI clients.
this.rebuildList();
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
g_GameSettings.playerCount.watch((_, oldNb) => this.OnPlayerNbChange(oldNb), ["nbPlayers"]);
// Sets up the dropdown and renders.
this.onPlayerAssignmentsChange();
this.playerAssignmentsControl.registerClientJoinHandler(this.onClientJoin.bind(this));
}
setControl()
@@ -41,13 +40,6 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
this.label = Engine.GetGUIObjectByName("playerAssignmentText[" + this.playerIndex + "]");
}
onLoad(initData, hotloadData)
{
if (!hotloadData && !g_IsNetworked)
this.onClientJoin("local", g_PlayerAssignments);
this.playerAssignmentsControl.updatePlayerAssignments();
}
OnPlayerNbChange(oldNb)
{
let isPlayerSlot = Object.values(g_PlayerAssignments).some(x => x.player === this.playerIndex + 1);
@@ -64,30 +56,10 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
}
}
onClientJoin(newGUID, newAssignments)
{
if (!g_IsController || this.fixedAI || newAssignments[newGUID].player != -1)
return;
// Assign the client (or only buddies if prefered) to a free slot
if (newGUID != Engine.GetPlayerGUID())
{
let assignOption = Engine.ConfigDB_GetValue("user", this.ConfigAssignPlayers);
if (assignOption == "disabled" ||
assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(newAssignments[newGUID].name).nick) == -1)
return;
}
for (let guid in newAssignments)
if (newAssignments[guid].player == this.playerIndex + 1)
return;
newAssignments[newGUID].player = this.playerIndex + 1;
this.playerAssignmentsControl.assignClient(newGUID, this.playerIndex + 1);
}
onPlayerAssignmentsChange()
{
// Rebuild the list to account for new/removed players.
this.rebuildList();
let newGUID;
for (let guid in g_PlayerAssignments)
if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
@@ -95,8 +67,14 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
newGUID = guid;
break;
}
if (this.assignedGUID === newGUID)
return;
this.assignedGUID = newGUID;
this.rebuildList();
if (this.assignedGUID && g_GameSettings.playerAI.get(this.playerIndex))
{
g_GameSettings.playerAI.setAI(this.playerIndex, undefined);
this.gameSettingsControl.setNetworkInitAttributes();
}
this.render();
}
@@ -121,6 +99,7 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
rebuildList()
{
Engine.ProfileStart("updatePlayerAssignmentsList");
// TODO: this particular bit is done for each row, which is unnecessarily inefficient.
this.playerItems = sortGUIDsByPlayerID().map(
this.clientItemFactory.createItem.bind(this.clientItemFactory));
this.values = prepareForDropdown([
@@ -129,8 +108,10 @@ PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSett
this.unassignedItem
]);
let selected = this.dropdown.list_data?.[this.dropdown.selected];
this.dropdown.list = this.values.Caption;
this.dropdown.list_data = this.values.Value;
this.setSelectedValue(selected);
Engine.ProfileStop();
}
@@ -154,9 +135,6 @@ PlayerSettingControls.PlayerAssignment.prototype.Tooltip =
PlayerSettingControls.PlayerAssignment.prototype.AutocompleteOrder = 100;
PlayerSettingControls.PlayerAssignment.prototype.ConfigAssignPlayers =
"gui.gamesetup.assignplayers";
{
PlayerAssignmentItem.Client = class
{
@@ -4,7 +4,7 @@ GameSettingControls.LockedTeams = class LockedTeams extends GameSettingControlCh
{
super(...args);
g_GameSettings.map.watch(() => this.render(), ["type"]);
g_GameSettings.rating.watch(() => this.render(), ["available", "enabled"]);
g_GameSettings.lockedTeams.watch(() => this.render(), ["available", "enabled"]);
this.render();
}