mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-10 08:10:52 +00:00
Add reference counting for emote sets so that we know when to unload them. Create a class for Users so that we can use ref counting with them.
This commit is contained in:
parent
b04cd8a61a
commit
6c4966166a
5 changed files with 256 additions and 29 deletions
|
@ -65,6 +65,8 @@ export default class Emotes extends Module {
|
||||||
this.global_sets = new SourcedSet;
|
this.global_sets = new SourcedSet;
|
||||||
|
|
||||||
this.emote_sets = {};
|
this.emote_sets = {};
|
||||||
|
this._set_refs = {};
|
||||||
|
this._set_timers = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnable() {
|
onEnable() {
|
||||||
|
@ -121,7 +123,44 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// FFZ Emote Sets
|
// Emote Set Ref Counting
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
addDefaultSet(provider, set_id, data) {
|
||||||
|
if ( ! this.default_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.default_sets.push(provider, set_id);
|
||||||
|
this.refSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data )
|
||||||
|
this.loadSetData(set_id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDefaultSet(provider, set_id) {
|
||||||
|
if ( this.default_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.default_sets.remove(provider, set_id);
|
||||||
|
this.unrefSet(set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refSet(set_id) {
|
||||||
|
this._set_refs[set_id] = (this._set_refs[set_id] || 0) + 1;
|
||||||
|
if ( this._set_timers[set_id] ) {
|
||||||
|
clearTimeout(this._set_timers[set_id]);
|
||||||
|
this._set_timers[set_id] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
unrefSet(set_id) {
|
||||||
|
const c = this._set_refs[set_id] = (this._set_refs[set_id] || 1) - 1;
|
||||||
|
if ( c <= 0 && ! this._set_timers[set_id] )
|
||||||
|
this._set_timers[set_id] = setTimeout(() => this.unloadSet(set_id), 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Emote Set Loading
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
async loadGlobalSets(tries = 0) {
|
async loadGlobalSets(tries = 0) {
|
||||||
|
@ -149,13 +188,12 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
const sets = data.sets || {};
|
const sets = data.sets || {};
|
||||||
|
|
||||||
this.default_sets.extend('ffz-global', ...data.default_sets);
|
for(const set_id of data.default_sets)
|
||||||
|
this.addDefaultSet('ffz-global', set_id);
|
||||||
|
|
||||||
for(const set_id in sets)
|
for(const set_id in sets)
|
||||||
if ( has(sets, set_id) ) {
|
if ( has(sets, set_id) )
|
||||||
this.global_sets.push('ffz-global', set_id);
|
|
||||||
this.loadSetData(set_id, sets[set_id]);
|
this.loadSetData(set_id, sets[set_id]);
|
||||||
}
|
|
||||||
|
|
||||||
if ( data.users )
|
if ( data.users )
|
||||||
this.loadSetUsers(data.users);
|
this.loadSetUsers(data.users);
|
||||||
|
@ -168,12 +206,9 @@ export default class Emotes extends Module {
|
||||||
const emote_set = this.emote_sets[set_id],
|
const emote_set = this.emote_sets[set_id],
|
||||||
users = data[set_id];
|
users = data[set_id];
|
||||||
|
|
||||||
for(const login of users) {
|
for(const login of users)
|
||||||
const user = this.parent.getUser(undefined, login),
|
this.parent.getUser(undefined, login)
|
||||||
sets = user.emote_sets;
|
.addSet('ffz-global', set_id);
|
||||||
|
|
||||||
sets.push('ffz-global', set_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.info(`Added "${emote_set ? emote_set.title : set_id}" emote set to ${users.length} users.`);
|
this.log.info(`Added "${emote_set ? emote_set.title : set_id}" emote set to ${users.length} users.`);
|
||||||
}
|
}
|
||||||
|
@ -191,8 +226,6 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
this.emote_sets[set_id] = data;
|
this.emote_sets[set_id] = data;
|
||||||
|
|
||||||
data.users = old_set ? old_set.users : 0;
|
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const ems = data.emotes || data.emoticons,
|
const ems = data.emotes || data.emoticons,
|
||||||
new_ems = data.emotes = {},
|
new_ems = data.emotes = {},
|
||||||
|
@ -237,11 +270,30 @@ export default class Emotes extends Module {
|
||||||
else if ( css.length )
|
else if ( css.length )
|
||||||
data.pending_css = css.join('');
|
data.pending_css = css.join('');
|
||||||
|
|
||||||
this.log.info(`Updated emotes for set #${set_id}: ${data.title}`);
|
this.log.info(`Loaded emote set #${set_id}: ${data.title} (${count} emotes)`);
|
||||||
this.emit(':loaded', set_id, data);
|
this.emit(':loaded', set_id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unloadSet(set_id, force = false) {
|
||||||
|
const old_set = this.emote_sets[set_id],
|
||||||
|
count = this._set_refs[set_id] || 0;
|
||||||
|
|
||||||
|
if ( ! old_set )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( count > 0 ) {
|
||||||
|
if ( ! force )
|
||||||
|
return this.log.warn(`Attempted to unload emote set #${set_id} with ${count} users.`);
|
||||||
|
this.log.warn(`Unloading emote set ${set_id} with ${count} users.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log.info(`Unloaded emote set #${set_id}: ${old_set.title}`);
|
||||||
|
this.emit(':unloaded', set_id, old_set);
|
||||||
|
this.emote_sets[set_id] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Emote CSS
|
// Emote CSS
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
|
@ -9,12 +9,13 @@ const WEBKIT = IS_WEBKIT ? '-webkit-' : '';
|
||||||
|
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import {createElement, ManagedStyle} from 'utilities/dom';
|
import {createElement, ManagedStyle} from 'utilities/dom';
|
||||||
import {timeout, has, SourcedSet} from 'utilities/object';
|
import {timeout, has} from 'utilities/object';
|
||||||
|
|
||||||
import Badges from './badges';
|
import Badges from './badges';
|
||||||
import Emotes from './emotes';
|
import Emotes from './emotes';
|
||||||
|
|
||||||
import Room from './room';
|
import Room from './room';
|
||||||
|
import User from './user';
|
||||||
import * as TOKENIZERS from './tokenizers';
|
import * as TOKENIZERS from './tokenizers';
|
||||||
|
|
||||||
|
|
||||||
|
@ -308,6 +309,8 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
getUser(id, login, no_create, no_login) {
|
getUser(id, login, no_create, no_login) {
|
||||||
let user;
|
let user;
|
||||||
|
if ( id && typeof id === 'number' )
|
||||||
|
id = `${id}`;
|
||||||
|
|
||||||
if ( this.user_ids[id] )
|
if ( this.user_ids[id] )
|
||||||
user = this.user_ids[id];
|
user = this.user_ids[id];
|
||||||
|
@ -319,7 +322,7 @@ export default class Chat extends Module {
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
else
|
else
|
||||||
user = {id, login, badges: [], emote_sets: new SourcedSet};
|
user = new User(this, null, id, login);
|
||||||
|
|
||||||
if ( id && id !== user.id ) {
|
if ( id && id !== user.id ) {
|
||||||
// If the ID isn't what we expected, something is very wrong here.
|
// If the ID isn't what we expected, something is very wrong here.
|
||||||
|
@ -328,7 +331,7 @@ export default class Chat extends Module {
|
||||||
throw new Error('id mismatch');
|
throw new Error('id mismatch');
|
||||||
|
|
||||||
// Otherwise, we're just here to set the ID.
|
// Otherwise, we're just here to set the ID.
|
||||||
user.id = id;
|
user._id = id;
|
||||||
this.user_ids[id] = user;
|
this.user_ids[id] = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,6 +357,8 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
getRoom(id, login, no_create, no_login) {
|
getRoom(id, login, no_create, no_login) {
|
||||||
let room;
|
let room;
|
||||||
|
if ( id && typeof id === 'number' )
|
||||||
|
id = `${id}`;
|
||||||
|
|
||||||
if ( this.room_ids[id] )
|
if ( this.room_ids[id] )
|
||||||
room = this.room_ids[id];
|
room = this.room_ids[id];
|
||||||
|
@ -374,7 +379,7 @@ export default class Chat extends Module {
|
||||||
throw new Error('id mismatch');
|
throw new Error('id mismatch');
|
||||||
|
|
||||||
// Otherwise, we're just here to set the ID.
|
// Otherwise, we're just here to set the ID.
|
||||||
room.id = id;
|
room._id = id;
|
||||||
this.room_ids[id] = room;
|
this.room_ids[id] = room;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,19 +4,18 @@
|
||||||
// Room
|
// Room
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
import User from './user';
|
||||||
|
|
||||||
import {API_SERVER, IS_WEBKIT} from 'utilities/constants';
|
import {API_SERVER, IS_WEBKIT} from 'utilities/constants';
|
||||||
|
|
||||||
import {EventEmitter} from 'utilities/events';
|
|
||||||
import {ManagedStyle} from 'utilities/dom';
|
import {ManagedStyle} from 'utilities/dom';
|
||||||
import {has, SourcedSet} from 'utilities/object';
|
import {has, SourcedSet} from 'utilities/object';
|
||||||
|
|
||||||
const WEBKIT = IS_WEBKIT ? '-webkit-' : '';
|
const WEBKIT = IS_WEBKIT ? '-webkit-' : '';
|
||||||
|
|
||||||
|
|
||||||
export default class Room extends EventEmitter {
|
export default class Room {
|
||||||
constructor(manager, id, login) {
|
constructor(manager, id, login) {
|
||||||
super();
|
|
||||||
|
|
||||||
this._destroy_timer = null;
|
this._destroy_timer = null;
|
||||||
|
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
@ -30,8 +29,8 @@ export default class Room extends EventEmitter {
|
||||||
this.style = new ManagedStyle(`room--${login}`);
|
this.style = new ManagedStyle(`room--${login}`);
|
||||||
|
|
||||||
this.emote_sets = new SourcedSet;
|
this.emote_sets = new SourcedSet;
|
||||||
this.users = [];
|
this.users = {};
|
||||||
this.user_ids = [];
|
this.user_ids = {};
|
||||||
|
|
||||||
this.manager.emit(':room-add', this);
|
this.manager.emit(':room-add', this);
|
||||||
this.load_data();
|
this.load_data();
|
||||||
|
@ -48,6 +47,22 @@ export default class Room extends EventEmitter {
|
||||||
|
|
||||||
this.style.destroy();
|
this.style.destroy();
|
||||||
|
|
||||||
|
for(const user of Object.values(this.user_ids)) {
|
||||||
|
if ( user )
|
||||||
|
user.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const user of Object.values(this.users)) {
|
||||||
|
if ( user )
|
||||||
|
user.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const set_id of this.emote_sets._cache)
|
||||||
|
this.manager.emotes.unrefSet(set_id);
|
||||||
|
|
||||||
|
this.emote_sets = null;
|
||||||
|
this.style = null;
|
||||||
|
|
||||||
if ( this._login ) {
|
if ( this._login ) {
|
||||||
if ( this.manager.rooms[this._login] === this )
|
if ( this.manager.rooms[this._login] === this )
|
||||||
this.manager.rooms[this._login] = null;
|
this.manager.rooms[this._login] = null;
|
||||||
|
@ -74,11 +89,10 @@ export default class Room extends EventEmitter {
|
||||||
|
|
||||||
if ( this._login ) {
|
if ( this._login ) {
|
||||||
const old_room = this.manager.rooms[this._login];
|
const old_room = this.manager.rooms[this._login];
|
||||||
if ( old_room !== this )
|
if ( old_room === this ) {
|
||||||
old_room.login = null;
|
this.manager.rooms[this._login] = null;
|
||||||
|
this.manager.socket.unsubscribe(`room.${this.login}`);
|
||||||
this.manager.rooms[this._login] = null;
|
}
|
||||||
this.manager.socket.unsubscribe(`room.${this.login}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._login = val;
|
this._login = val;
|
||||||
|
@ -95,6 +109,54 @@ export default class Room extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getUser(id, login, no_create, no_login) {
|
||||||
|
let user;
|
||||||
|
if ( id && typeof id === 'number' )
|
||||||
|
id = `${id}`;
|
||||||
|
|
||||||
|
if ( this.user_ids[id] )
|
||||||
|
user = this.user_ids[id];
|
||||||
|
|
||||||
|
else if ( this.users[login] && ! no_login )
|
||||||
|
user = this.users[login];
|
||||||
|
|
||||||
|
else if ( no_create )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
else
|
||||||
|
user = new User(this.manager, this, id, login);
|
||||||
|
|
||||||
|
if ( id && id !== user.id ) {
|
||||||
|
// If the ID isn't what we expected, something is very wrong here.
|
||||||
|
// Blame name changes.
|
||||||
|
if ( user.id )
|
||||||
|
throw new Error('id mismatch');
|
||||||
|
|
||||||
|
// Otherwise, we're just here to set the ID.
|
||||||
|
user._id = id;
|
||||||
|
this.user_ids[id] = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( login ) {
|
||||||
|
const other = this.users[login];
|
||||||
|
if ( other ) {
|
||||||
|
if ( other !== this && ! no_login ) {
|
||||||
|
// If the other has an ID, something weird happened. Screw it
|
||||||
|
// and just take over.
|
||||||
|
if ( other.id )
|
||||||
|
this.users[login] = user;
|
||||||
|
else {
|
||||||
|
// TODO: Merge Logic~~
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
this.users[login] = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// FFZ Data
|
// FFZ Data
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
@ -163,6 +225,27 @@ export default class Room extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Emote Sets
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
addSet(provider, set_id, data) {
|
||||||
|
if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.emote_sets.push(provider, set_id);
|
||||||
|
this.manager.emotes.refSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data )
|
||||||
|
this.manager.emotes.loadSetData(set_id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSet(provider, set_id) {
|
||||||
|
if ( this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.emote_sets.remove(provider, set_id);
|
||||||
|
this.manager.emotes.unrefSet(set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Life Cycle
|
// Life Cycle
|
||||||
|
|
82
src/modules/chat/user.js
Normal file
82
src/modules/chat/user.js
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// User
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
import {SourcedSet} from 'utilities/object';
|
||||||
|
|
||||||
|
export default class User {
|
||||||
|
constructor(manager, room, id, login) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.room = room;
|
||||||
|
|
||||||
|
this._id = id;
|
||||||
|
this.login = login;
|
||||||
|
|
||||||
|
if ( id )
|
||||||
|
(room || manager).user_ids[id] = this;
|
||||||
|
|
||||||
|
this.emote_sets = new SourcedSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.destroyed = true;
|
||||||
|
|
||||||
|
for(const set_id of this.emote_sets._cache)
|
||||||
|
this.manager.emotes.unrefSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
get login() {
|
||||||
|
return this._login;
|
||||||
|
}
|
||||||
|
|
||||||
|
set login(val) {
|
||||||
|
if ( this._login === val )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const obj = this.room || this.manager;
|
||||||
|
|
||||||
|
if ( this._login ) {
|
||||||
|
const old_user = obj.users[this._login];
|
||||||
|
if ( old_user === this )
|
||||||
|
obj.users[this._login] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._login = val;
|
||||||
|
if ( ! val )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const old_user = obj.users[val];
|
||||||
|
if ( old_user && old_user !== this )
|
||||||
|
old_user.login = null;
|
||||||
|
|
||||||
|
obj.users[val] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Emote Sets
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
addSet(provider, set_id, data) {
|
||||||
|
if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.emote_sets.push(provider, set_id);
|
||||||
|
this.manager.emotes.refSet(set_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data )
|
||||||
|
this.manager.emotes.loadSetData(set_id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSet(provider, set_id) {
|
||||||
|
if ( this.emote_sets.sourceIncludes(provider, set_id) ) {
|
||||||
|
this.emote_sets.remove(provider, set_id);
|
||||||
|
this.manager.emotes.unrefSet(set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -186,6 +186,11 @@ export class SourcedSet {
|
||||||
get(key) { return this._sources && this._sources.get(key) }
|
get(key) { return this._sources && this._sources.get(key) }
|
||||||
has(key) { return this._sources ? this._sources.has(key) : false }
|
has(key) { return this._sources ? this._sources.has(key) : false }
|
||||||
|
|
||||||
|
sourceIncludes(key, val) {
|
||||||
|
const src = this._sources && this._sources.get(key);
|
||||||
|
return src && src.includes(val);
|
||||||
|
}
|
||||||
|
|
||||||
includes(val) {
|
includes(val) {
|
||||||
return this._cache.includes(val);
|
return this._cache.includes(val);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue