mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-11-08 10:29:00 +00:00
This allows the same user to keep the buddy list across clients and config resets. At the same time this allows for per lobby buddy lists which is needed as the same nickname may refer to different people. Use lower case comparison for buddy names. This is in line with the idea of local part of jid should be seen as normalized nick and is used server-side to compare user names. The migration is kept simple. I.e. only attempted if the roster is empty and the config indicates no migration was done before. Fixes: #6824 Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
241 lines
6.4 KiB
JavaScript
241 lines
6.4 KiB
JavaScript
/**
|
|
* This class provides a property independent interface to g_PlayerAssignment events and actions.
|
|
*/
|
|
class PlayerAssignmentsController
|
|
{
|
|
constructor(setupWindow, netMessages, isSavedGame)
|
|
{
|
|
this.clientJoinHandlers = new Set();
|
|
this.clientLeaveHandlers = new Set();
|
|
this.playerAssignmentsChangeHandlers = new Set();
|
|
|
|
if (!g_IsNetworked)
|
|
{
|
|
const name = singleplayerName();
|
|
|
|
// Replace empty player name when entering a single-player match for the first time.
|
|
Engine.ConfigDB_CreateAndSaveValue("user", this.ConfigNameSingleplayer, name);
|
|
|
|
// By default, assign the player to the first slot.
|
|
g_PlayerAssignments = {
|
|
"local": {
|
|
"name": name,
|
|
"player": 1
|
|
}
|
|
};
|
|
}
|
|
|
|
// Keep a list of last assigned slot for each player, so we can try to re-assign them
|
|
// if they disconnect/rejoin.
|
|
this.lastAssigned = {};
|
|
|
|
g_GameSettings.playerCount.watch(() => this.unassignInvalidPlayers(), ["nbPlayers"]);
|
|
|
|
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, isSavedGame));
|
|
}
|
|
|
|
registerPlayerAssignmentsChangeHandler(handler)
|
|
{
|
|
this.playerAssignmentsChangeHandlers.add(handler);
|
|
}
|
|
|
|
unregisterPlayerAssignmentsChangeHandler(handler)
|
|
{
|
|
this.playerAssignmentsChangeHandlers.delete(handler);
|
|
}
|
|
|
|
registerClientJoinHandler(handler)
|
|
{
|
|
this.clientJoinHandlers.add(handler);
|
|
}
|
|
|
|
unregisterClientJoinHandler(handler)
|
|
{
|
|
this.clientJoinHandlers.delete(handler);
|
|
}
|
|
|
|
registerClientLeaveHandler(handler)
|
|
{
|
|
this.clientLeaveHandlers.add(handler);
|
|
}
|
|
|
|
unregisterClientLeaveHandler(handler)
|
|
{
|
|
this.clientLeaveHandlers.delete(handler);
|
|
}
|
|
|
|
onLoad(initData, hotloadData)
|
|
{
|
|
if (hotloadData)
|
|
{
|
|
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)
|
|
{
|
|
object.playerAssignments = g_PlayerAssignments;
|
|
}
|
|
|
|
/**
|
|
* On client join, try to assign them to a free slot.
|
|
* (This is called before g_PlayerAssignments is updated).
|
|
*/
|
|
onClientJoin(isSavedGame, 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())
|
|
{
|
|
const assignOption = Engine.ConfigDB_GetValue("user", this.ConfigAssignPlayers);
|
|
if (assignOption == "disabled" ||
|
|
assignOption == "buddies" &&
|
|
!Engine.IsLobbyBuddy(splitRatingFromNick(newAssignments[newGUID].name).nick))
|
|
return;
|
|
}
|
|
|
|
const slot = this.findAssignmentSlot(isSavedGame, newGUID, newAssignments);
|
|
|
|
if (slot === undefined)
|
|
return;
|
|
|
|
this.assignClient(newGUID, slot);
|
|
}
|
|
|
|
findAssignmentSlot(isSavedGame, newGUID, newAssignments)
|
|
{
|
|
const newAssignmentsArray = Object.values(newAssignments);
|
|
const isSlotAvailable = slot =>
|
|
newAssignmentsArray.every(elem => elem.player !== slot) &&
|
|
(!isSavedGame || !g_GameSettings.playerAI.get(slot - 1));
|
|
|
|
const newName = newAssignments[newGUID].name;
|
|
// First check if we know them and try to give them their old assignment back.
|
|
if (this.lastAssigned[newName] > 0 &&
|
|
this.lastAssigned[newName] <= g_GameSettings.playerCount.nbPlayers &&
|
|
isSlotAvailable(this.lastAssigned[newName]))
|
|
{
|
|
return this.lastAssigned[newName];
|
|
}
|
|
|
|
// If we can't restore the previous slot, try to give them the slot stored in the savegame.
|
|
if (isSavedGame)
|
|
{
|
|
const saveSlot = g_GameSettings.playerName.values.indexOf(newName) + 1;
|
|
if (saveSlot > 0 && isSlotAvailable(saveSlot))
|
|
return saveSlot;
|
|
}
|
|
|
|
// If we can't restore the slot, assign the client to the first free slot.
|
|
return Array.from(Array(g_GameSettings.playerCount.nbPlayers).keys(), i => i + 1).find(
|
|
isSlotAvailable);
|
|
}
|
|
|
|
/**
|
|
* To be called when g_PlayerAssignments is modified.
|
|
*/
|
|
updatePlayerAssignments()
|
|
{
|
|
Engine.ProfileStart("updatePlayerAssignments");
|
|
for (const guid in g_PlayerAssignments)
|
|
this.lastAssigned[g_PlayerAssignments[guid].name] = g_PlayerAssignments[guid].player;
|
|
for (const handler of this.playerAssignmentsChangeHandlers)
|
|
handler();
|
|
Engine.ProfileStop();
|
|
}
|
|
|
|
/**
|
|
* Called whenever a client joins or leaves or any game setting is changed.
|
|
*/
|
|
onPlayerAssignmentMessage(message)
|
|
{
|
|
const newAssignments = message.newAssignments;
|
|
for (const guid in newAssignments)
|
|
if (!g_PlayerAssignments[guid])
|
|
for (const handler of this.clientJoinHandlers)
|
|
handler(guid, message.newAssignments);
|
|
|
|
for (const guid in g_PlayerAssignments)
|
|
if (!newAssignments[guid])
|
|
for (const handler of this.clientLeaveHandlers)
|
|
handler(guid);
|
|
|
|
g_PlayerAssignments = newAssignments;
|
|
this.updatePlayerAssignments();
|
|
}
|
|
|
|
assignClient(guid, playerIndex)
|
|
{
|
|
if (g_IsNetworked)
|
|
Engine.AssignNetworkPlayer(playerIndex, guid);
|
|
else
|
|
{
|
|
g_PlayerAssignments[guid].player = playerIndex;
|
|
this.updatePlayerAssignments();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If both clients are assigned players, this will swap their assignments.
|
|
*/
|
|
assignPlayer(guidToAssign, playerIndex)
|
|
{
|
|
if (g_PlayerAssignments[guidToAssign].player != -1)
|
|
{
|
|
for (const guid in g_PlayerAssignments)
|
|
if (g_PlayerAssignments[guid].player == playerIndex + 1)
|
|
{
|
|
this.assignClient(guid, g_PlayerAssignments[guidToAssign].player);
|
|
break;
|
|
}
|
|
}
|
|
this.assignClient(guidToAssign, playerIndex + 1);
|
|
}
|
|
|
|
unassignClient(playerID)
|
|
{
|
|
if (g_IsNetworked)
|
|
Engine.AssignNetworkPlayer(playerID, "");
|
|
else if (g_PlayerAssignments.local.player == playerID)
|
|
{
|
|
g_PlayerAssignments.local.player = -1;
|
|
this.updatePlayerAssignments();
|
|
}
|
|
}
|
|
|
|
unassignInvalidPlayers()
|
|
{
|
|
if (g_IsNetworked)
|
|
{
|
|
for (const 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;
|
|
this.updatePlayerAssignments();
|
|
}
|
|
}
|
|
}
|
|
|
|
PlayerAssignmentsController.prototype.ConfigNameSingleplayer =
|
|
"playername.singleplayer";
|
|
|
|
PlayerAssignmentsController.prototype.ConfigAssignPlayers =
|
|
"gui.gamesetup.assignplayers";
|