142 lines
4.0 KiB
JavaScript
142 lines
4.0 KiB
JavaScript
/**
|
|
* The singleton collection of User documents which exist within the active World.
|
|
* This Collection is accessible within the Game object as game.users.
|
|
* @extends {WorldCollection}
|
|
*
|
|
* @see {@link User} The User document
|
|
*/
|
|
class Users extends WorldCollection {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
/**
|
|
* The User document of the currently connected user
|
|
* @type {User|null}
|
|
*/
|
|
this.current = this.current || null;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Initialize the Map object and all its contained documents
|
|
* @private
|
|
* @override
|
|
*/
|
|
_initialize() {
|
|
super._initialize();
|
|
|
|
// Flag the current user
|
|
this.current = this.get(game.data.userId) || null;
|
|
if ( this.current ) this.current.active = true;
|
|
|
|
// Set initial user activity state
|
|
for ( let activeId of game.data.activeUsers || [] ) {
|
|
this.get(activeId).active = true;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static documentName = "User";
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Get the users with player roles
|
|
* @returns {User[]}
|
|
*/
|
|
get players() {
|
|
return this.filter(u => !u.isGM && u.hasRole("PLAYER"));
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Get one User who is an active Gamemaster (non-assistant if possible), or null if no active GM is available.
|
|
* This can be useful for workflows which occur on all clients, but where only one user should take action.
|
|
* @type {User|null}
|
|
*/
|
|
get activeGM() {
|
|
const activeGMs = game.users.filter(u => u.active && u.isGM);
|
|
activeGMs.sort((a, b) => (b.role - a.role) || a.id.compare(b.id)); // Alphanumeric sort IDs without using localeCompare
|
|
return activeGMs[0] || null;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Socket Listeners and Handlers */
|
|
/* -------------------------------------------- */
|
|
|
|
static _activateSocketListeners(socket) {
|
|
socket.on("userActivity", this._handleUserActivity);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Handle receipt of activity data from another User connected to the Game session
|
|
* @param {string} userId The User id who generated the activity data
|
|
* @param {ActivityData} activityData The object of activity data
|
|
* @private
|
|
*/
|
|
static _handleUserActivity(userId, activityData={}) {
|
|
const user = game.users.get(userId);
|
|
if ( !user || user.isSelf ) return;
|
|
|
|
// Update User active state
|
|
const active = "active" in activityData ? activityData.active : true;
|
|
if ( user.active !== active ) {
|
|
user.active = active;
|
|
game.users.render();
|
|
ui.nav.render();
|
|
Hooks.callAll("userConnected", user, active);
|
|
}
|
|
|
|
// Everything below here requires the game to be ready
|
|
if ( !game.ready ) return;
|
|
|
|
// Set viewed scene
|
|
const sceneChange = ("sceneId" in activityData) && (activityData.sceneId !== user.viewedScene);
|
|
if ( sceneChange ) {
|
|
user.viewedScene = activityData.sceneId;
|
|
ui.nav.render();
|
|
}
|
|
|
|
if ( "av" in activityData ) {
|
|
game.webrtc.settings.handleUserActivity(userId, activityData.av);
|
|
}
|
|
|
|
// Everything below requires an active canvas
|
|
if ( !canvas.ready ) return;
|
|
|
|
// User control deactivation
|
|
if ( (active === false) || (user.viewedScene !== canvas.id) ) {
|
|
canvas.controls.updateCursor(user, null);
|
|
canvas.controls.updateRuler(user, null);
|
|
user.updateTokenTargets([]);
|
|
return;
|
|
}
|
|
|
|
// Cursor position
|
|
if ( "cursor" in activityData ) {
|
|
canvas.controls.updateCursor(user, activityData.cursor);
|
|
}
|
|
|
|
// Was it a ping?
|
|
if ( "ping" in activityData ) {
|
|
canvas.controls.handlePing(user, activityData.cursor, activityData.ping);
|
|
}
|
|
|
|
// Ruler measurement
|
|
if ( "ruler" in activityData ) {
|
|
canvas.controls.updateRuler(user, activityData.ruler);
|
|
}
|
|
|
|
// Token targets
|
|
if ( "targets" in activityData ) {
|
|
user.updateTokenTargets(activityData.targets);
|
|
}
|
|
}
|
|
}
|