Files
VRCX/src/service/database/gameLog.js
2026-02-01 23:33:13 +09:00

1510 lines
56 KiB
JavaScript

import { dbVars } from '../database';
import sqliteService from '../sqlite.js';
const gameLog = {
async getGamelogDatabase() {
var gamelogDatabase = [];
var date = new Date();
date.setDate(date.getDate() - 1); // 24 hour limit
var dateOffset = date.toJSON();
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Location',
location: dbRow[2],
worldId: dbRow[3],
worldName: dbRow[4],
time: dbRow[5],
groupName: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_location WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
displayName: dbRow[3],
location: dbRow[4],
userId: dbRow[5],
time: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_join_leave WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'PortalSpawn',
displayName: dbRow[2],
location: dbRow[3],
userId: dbRow[4],
instanceId: dbRow[5],
worldName: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_portal_spawn WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'VideoPlay',
videoUrl: dbRow[2],
videoName: dbRow[3],
videoId: dbRow[4],
location: dbRow[5],
displayName: dbRow[6],
userId: dbRow[7]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_video_play WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[3],
resourceUrl: dbRow[2],
location: dbRow[4]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_resource_load WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Event',
data: dbRow[2]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_event WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'External',
message: dbRow[2],
displayName: dbRow[3],
userId: dbRow[4],
location: dbRow[5]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_external WHERE created_at >= date('${dateOffset}') ORDER BY id DESC LIMIT ${dbVars.maxTableSize}`);
var compareByCreatedAt = function (a, b) {
var A = a.created_at;
var B = b.created_at;
if (A < B) {
return -1;
}
if (A > B) {
return 1;
}
return 0;
};
gamelogDatabase.sort(compareByCreatedAt);
if (gamelogDatabase.length > dbVars.maxTableSize) {
gamelogDatabase.splice(
0,
gamelogDatabase.length - dbVars.maxTableSize
);
}
return gamelogDatabase;
},
addGamelogLocationToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_location (created_at, location, world_id, world_name, time, group_name) VALUES (@created_at, @location, @world_id, @world_name, @time, @group_name)`,
{
'@created_at': entry.created_at,
'@location': entry.location,
'@world_id': entry.worldId,
'@world_name': entry.worldName,
'@time': entry.time,
'@group_name': entry.groupName
}
);
},
updateGamelogLocationTimeToDatabase(entry) {
sqliteService.executeNonQuery(
`UPDATE gamelog_location SET time = @time WHERE created_at = @created_at`,
{
'@created_at': entry.created_at,
'@time': entry.time
}
);
},
addGamelogJoinLeaveToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_join_leave (created_at, type, display_name, location, user_id, time) VALUES (@created_at, @type, @display_name, @location, @user_id, @time)`,
{
'@created_at': entry.created_at,
'@type': entry.type,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@time': entry.time
}
);
},
addGamelogJoinLeaveBulk(inputData) {
if (inputData.length === 0) {
return;
}
var sqlValues = '';
var items = [
'created_at',
'type',
'displayName',
'location',
'userId',
'time'
];
for (var line of inputData) {
var field = {};
for (var item of items) {
if (typeof line[item] === 'string') {
field[item] = line[item].replace(/'/g, "''");
} else if (typeof line[item] === 'number') {
field[item] = line[item];
} else {
field[item] = '';
}
}
sqlValues += `('${field.created_at}', '${field.type}', '${field.displayName}', '${field.location}', '${field.userId}', '${field.time}'), `;
}
sqlValues = sqlValues.slice(0, -2);
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_join_leave (created_at, type, display_name, location, user_id, time) VALUES ${sqlValues}`
);
},
addGamelogPortalSpawnToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_portal_spawn (created_at, display_name, location, user_id, instance_id, world_name) VALUES (@created_at, @display_name, @location, @user_id, @instance_id, @world_name)`,
{
'@created_at': entry.created_at,
'@display_name': entry.displayName,
'@location': entry.location,
'@user_id': entry.userId,
'@instance_id': entry.instanceId,
'@world_name': entry.worldName
}
);
},
addGamelogVideoPlayToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_video_play (created_at, video_url, video_name, video_id, location, display_name, user_id) VALUES (@created_at, @video_url, @video_name, @video_id, @location, @display_name, @user_id)`,
{
'@created_at': entry.created_at,
'@video_url': entry.videoUrl,
'@video_name': entry.videoName,
'@video_id': entry.videoId,
'@location': entry.location,
'@display_name': entry.displayName,
'@user_id': entry.userId
}
);
},
addGamelogResourceLoadToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_resource_load (created_at, resource_url, resource_type, location) VALUES (@created_at, @resource_url, @resource_type, @location)`,
{
'@created_at': entry.created_at,
'@resource_url': entry.resourceUrl,
'@resource_type': entry.type,
'@location': entry.location
}
);
},
addGamelogEventToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_event (created_at, data) VALUES (@created_at, @data)`,
{
'@created_at': entry.created_at,
'@data': entry.data
}
);
},
addGamelogExternalToDatabase(entry) {
sqliteService.executeNonQuery(
`INSERT OR IGNORE INTO gamelog_external (created_at, message, display_name, user_id, location) VALUES (@created_at, @message, @display_name, @user_id, @location)`,
{
'@created_at': entry.created_at,
'@message': entry.message,
'@display_name': entry.displayName,
'@user_id': entry.userId,
'@location': entry.location
}
);
},
async getLastVisit(worldId, currentWorldMatch) {
var count = currentWorldMatch ? 2 : 1;
var ref = {
created_at: '',
worldId: ''
};
await sqliteService.execute(
(row) => {
ref = {
created_at: row[0],
worldId: row[1]
};
},
`SELECT created_at, world_id FROM gamelog_location WHERE world_id = @worldId ORDER BY id DESC LIMIT @count`,
{
'@worldId': worldId,
'@count': count
}
);
return ref;
},
async getVisitCount(worldId) {
var ref = {
visitCount: 0,
worldId: ''
};
await sqliteService.execute(
(row) => {
ref = {
visitCount: row[0],
worldId
};
},
`SELECT COUNT(DISTINCT location) FROM gamelog_location WHERE world_id = @worldId`,
{
'@worldId': worldId
}
);
return ref;
},
async getTimeSpentInWorld(worldId) {
var ref = {
timeSpent: 0,
worldId
};
await sqliteService.execute(
(row) => {
if (typeof row[0] === 'number') {
ref.timeSpent += row[0];
}
},
`SELECT time FROM gamelog_location WHERE world_id = @worldId`,
{
'@worldId': worldId
}
);
return ref;
},
async getLastGroupVisit(groupName) {
var ref = {
created_at: '',
groupName: ''
};
await sqliteService.execute(
(row) => {
ref = {
created_at: row[0],
groupName: row[1]
};
},
`SELECT created_at, group_name FROM gamelog_location WHERE group_name = @groupName ORDER BY id DESC LIMIT 1`,
{
'@groupName': groupName
}
);
return ref;
},
async getPreviousInstancesByGroupId(groupId) {
const data = new Map();
await sqliteService.execute(
(dbRow) => {
let time = 0;
if (dbRow[2]) {
time = dbRow[2];
}
var ref = data.get(dbRow[1]);
if (typeof ref !== 'undefined') {
time += ref.time;
}
var row = {
created_at: dbRow[0],
location: dbRow[1],
time,
worldName: dbRow[3],
groupName: dbRow[4]
};
data.set(row.location, row);
},
`SELECT created_at, location, time, world_name, group_name FROM gamelog_location WHERE location LIKE '%${groupId}%' ORDER BY id DESC`,
{
'@groupId': groupId
}
);
return data;
},
async getLastSeen(input, inCurrentWorld) {
const count = inCurrentWorld ? 2 : 1;
let ref = {
created_at: '',
userId: ''
};
await sqliteService.execute(
(row) => {
if (row[1]) {
ref = {
created_at: row[0],
userId: row[1]
};
} else {
ref = {
created_at: row[0],
userId: input.id
};
}
},
`SELECT created_at, user_id FROM gamelog_join_leave WHERE user_id = @userId OR display_name = @displayName ORDER BY id DESC LIMIT @count`,
{
'@userId': input.id,
'@displayName': input.displayName,
'@count': count
}
);
return ref;
},
async getJoinCount(input) {
var ref = {
joinCount: '',
userId: ''
};
await sqliteService.execute(
(row) => {
if (row[1]) {
ref = {
joinCount: row[0],
userId: row[1]
};
} else {
ref = {
joinCount: row[0],
userId: input.id
};
}
},
`SELECT COUNT(DISTINCT location) FROM gamelog_join_leave WHERE (type = 'OnPlayerJoined') AND (user_id = @userId OR display_name = @displayName)`,
{
'@userId': input.id,
'@displayName': input.displayName
}
);
return ref;
},
async getTimeSpent(input) {
var ref = {
timeSpent: 0,
userId: input.id
};
await sqliteService.execute(
(row) => {
if (typeof row[0] === 'number') {
ref.timeSpent += row[0];
}
},
`SELECT time FROM gamelog_join_leave WHERE (type = 'OnPlayerLeft') AND (user_id = @userId OR display_name = @displayName)`,
{
'@userId': input.id,
'@displayName': input.displayName
}
);
return ref;
},
async getUserStats(input, inCurrentWorld) {
var i = 0;
var instances = new Set();
var ref = {
timeSpent: 0,
lastSeen: '',
joinCount: 0,
userId: input.id,
previousDisplayNames: new Map()
};
await sqliteService.execute(
(row) => {
if (typeof row[2] === 'number') {
ref.timeSpent += row[2];
}
i++;
if (i === 1 || (inCurrentWorld && i === 2)) {
ref.lastSeen = row[0];
}
instances.add(row[3]);
if (input.displayName !== row[4]) {
ref.previousDisplayNames.set(row[4], row[0]);
}
},
`SELECT created_at, user_id, time, location, display_name FROM gamelog_join_leave WHERE user_id = @userId OR display_name = @displayName ORDER BY id DESC`,
{
'@userId': input.id,
'@displayName': input.displayName
}
);
instances.delete('');
ref.joinCount = instances.size;
return ref;
},
async getAllUserStats(userIds, displayNames) {
var data = [];
// this makes me most sad
var userIdsString = '';
for (var userId of userIds) {
userIdsString += `'${userId}', `;
}
userIdsString = userIdsString.slice(0, -2);
var displayNamesString = '';
for (var displayName of displayNames) {
displayNamesString += `'${displayName.replaceAll("'", "''")}', `;
}
displayNamesString = displayNamesString.slice(0, -2);
await sqliteService.execute(
(dbRow) => {
var row = {
lastSeen: dbRow[0],
userId: dbRow[1],
timeSpent: dbRow[2],
joinCount: dbRow[3],
displayName: dbRow[4]
};
data.push(row);
},
`SELECT
g.created_at,
g.user_id,
SUM(g.time) AS timeSpent,
COUNT(DISTINCT g.location) AS joinCount,
g.display_name,
MAX(g.id) AS max_id
FROM
gamelog_join_leave g
WHERE
g.user_id IN (${userIdsString})
OR g.display_name IN (${displayNamesString})
GROUP BY
g.user_id,
g.display_name
ORDER BY
g.user_id DESC
`
);
return data;
},
async getGameLogByLocation(instanceId, filters) {
var gamelogDatabase = [];
var location = true;
var onplayerjoined = true;
var onplayerleft = true;
var portalspawn = true;
var videoplay = true;
var resourceload_string = true;
var resourceload_image = true;
if (filters.length > 0) {
location = false;
onplayerjoined = false;
onplayerleft = false;
portalspawn = false;
videoplay = false;
resourceload_string = false;
resourceload_image = false;
filters.forEach((filter) => {
switch (filter) {
case 'Location':
location = true;
break;
case 'OnPlayerJoined':
onplayerjoined = true;
break;
case 'OnPlayerLeft':
onplayerleft = true;
break;
case 'PortalSpawn':
portalspawn = true;
break;
case 'VideoPlay':
videoplay = true;
break;
case 'StringLoad':
resourceload_string = true;
break;
case 'ImageLoad':
resourceload_image = true;
break;
}
});
}
if (location) {
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'Location',
location: dbRow[2],
worldId: dbRow[3],
worldName: dbRow[4],
time: dbRow[5],
groupName: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_location WHERE location LIKE '%${instanceId}%' ORDER BY id DESC LIMIT ${dbVars.searchTableSize}`);
}
if (onplayerjoined || onplayerleft) {
var query = '';
if (!onplayerjoined || !onplayerleft) {
if (onplayerjoined) {
query = "AND type = 'OnPlayerJoined'";
} else if (onplayerleft) {
query = "AND type = 'OnPlayerLeft'";
}
}
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[2],
displayName: dbRow[3],
location: dbRow[4],
userId: dbRow[5],
time: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_join_leave WHERE (location LIKE '%${instanceId}%' AND user_id != '${dbVars.userId}') ${query} ORDER BY id DESC LIMIT ${dbVars.searchTableSize}`);
}
if (portalspawn) {
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'PortalSpawn',
displayName: dbRow[2],
location: dbRow[3],
userId: dbRow[4],
instanceId: dbRow[5],
worldName: dbRow[6]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_portal_spawn WHERE location LIKE '%${instanceId}%' ORDER BY id DESC LIMIT ${dbVars.searchTableSize}`);
}
if (videoplay) {
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: 'VideoPlay',
videoUrl: dbRow[2],
videoName: dbRow[3],
videoId: dbRow[4],
location: dbRow[5],
displayName: dbRow[6],
userId: dbRow[7]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_video_play WHERE location LIKE '%${instanceId}%' ORDER BY id DESC LIMIT ${dbVars.searchTableSize}`);
}
if (resourceload_string || resourceload_image) {
var checkString = '';
var checkImage = '';
if (!resourceload_string) {
checkString = `AND resource_type != 'StringLoad'`;
}
if (!resourceload_image) {
checkString = `AND resource_type != 'ImageLoad'`;
}
await sqliteService.execute((dbRow) => {
var row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[3],
resourceUrl: dbRow[2],
location: dbRow[4]
};
gamelogDatabase.push(row);
}, `SELECT * FROM gamelog_resource_load WHERE location LIKE '%${instanceId}%' ${checkString} ${checkImage} ORDER BY id DESC LIMIT ${dbVars.searchTableSize}`);
}
var compareByCreatedAt = function (a, b) {
var A = a.created_at;
var B = b.created_at;
if (A < B) {
return -1;
}
if (A > B) {
return 1;
}
return 0;
};
gamelogDatabase.sort(compareByCreatedAt);
gamelogDatabase.splice(
0,
gamelogDatabase.length - dbVars.searchTableSize
);
return gamelogDatabase;
},
async lookupGameLogDatabase(
filters,
vipList,
maxEntries = dbVars.maxTableSize
) {
const baseColumns = [
'id',
'created_at',
'type',
'display_name',
'location',
'user_id',
'time',
'world_id',
'world_name',
'group_name',
'instance_id',
'video_url',
'video_name',
'video_id',
'resource_url',
'resource_type',
'data',
'message'
].join(', ');
let vipQuery = '';
if (vipList.length > 0) {
vipQuery = 'AND user_id IN (';
for (var i = 0; i < vipList.length; i++) {
vipQuery += `'${vipList[i].replaceAll("'", "''")}'`;
if (i < vipList.length - 1) {
vipQuery += ', ';
}
}
vipQuery += ')';
}
let location = true;
let onplayerjoined = true;
let onplayerleft = true;
let portalspawn = true;
let msgevent = true;
let external = true;
let videoplay = true;
let resourceload_string = true;
let resourceload_image = true;
if (filters.length > 0) {
location = false;
onplayerjoined = false;
onplayerleft = false;
portalspawn = false;
msgevent = false;
external = false;
videoplay = false;
resourceload_string = false;
resourceload_image = false;
filters.forEach((filter) => {
switch (filter) {
case 'Location':
location = true;
break;
case 'OnPlayerJoined':
onplayerjoined = true;
break;
case 'OnPlayerLeft':
onplayerleft = true;
break;
case 'PortalSpawn':
portalspawn = true;
break;
case 'Event':
msgevent = true;
break;
case 'External':
external = true;
break;
case 'VideoPlay':
videoplay = true;
break;
case 'StringLoad':
resourceload_string = true;
break;
case 'ImageLoad':
resourceload_image = true;
break;
}
});
}
const selects = [];
if (location) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'Location' AS type, NULL AS display_name, location, NULL AS user_id, time, world_id, world_name, group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_location ORDER BY id DESC LIMIT @perTable)`
);
}
if (onplayerjoined || onplayerleft) {
let query = '';
if (!onplayerjoined || !onplayerleft) {
if (onplayerjoined) {
query = "AND type = 'OnPlayerJoined'";
} else if (onplayerleft) {
query = "AND type = 'OnPlayerLeft'";
}
}
selects.push(
`SELECT * FROM (SELECT id, created_at, type, display_name, location, user_id, time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_join_leave WHERE 1=1 ${vipQuery} ${query} ORDER BY id DESC LIMIT @perTable)`
);
}
if (portalspawn) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'PortalSpawn' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, world_name, NULL AS group_name, instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_portal_spawn WHERE 1=1 ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (msgevent) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'Event' AS type, NULL AS display_name, NULL AS location, NULL AS user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, data, NULL AS message FROM gamelog_event ORDER BY id DESC LIMIT @perTable)`
);
}
if (external) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'External' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, message FROM gamelog_external WHERE 1=1 ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (videoplay) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'VideoPlay' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, video_url, video_name, video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_video_play WHERE 1=1 ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (resourceload_string || resourceload_image) {
let checkString = '';
let checkImage = '';
if (!resourceload_string) {
checkString = `AND resource_type != 'StringLoad'`;
}
if (!resourceload_image) {
checkString = `AND resource_type != 'ImageLoad'`;
}
selects.push(
`SELECT * FROM (SELECT id, created_at, resource_type AS type, NULL AS display_name, location, NULL AS user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, resource_url, resource_type, NULL AS data, NULL AS message FROM gamelog_resource_load WHERE 1=1 ${checkString} ${checkImage} ORDER BY id DESC LIMIT @perTable)`
);
}
if (selects.length === 0) {
return [];
}
const gamelogDatabase = [];
const args = {
'@limit': maxEntries,
'@perTable': maxEntries
};
await sqliteService.execute(
(dbRow) => {
const row = {
rowId: dbRow[0],
created_at: dbRow[1],
type: dbRow[2]
};
switch (dbRow[2]) {
case 'Location':
row.location = dbRow[4];
row.worldId = dbRow[7];
row.worldName = dbRow[8];
row.time = dbRow[6];
row.groupName = dbRow[9];
break;
case 'OnPlayerJoined':
case 'OnPlayerLeft':
row.displayName = dbRow[3];
row.location = dbRow[4];
row.userId = dbRow[5];
row.time = dbRow[6];
break;
case 'PortalSpawn':
row.displayName = dbRow[3];
row.location = dbRow[4];
row.userId = dbRow[5];
row.instanceId = dbRow[10];
row.worldName = dbRow[8];
break;
case 'VideoPlay':
row.videoUrl = dbRow[11];
row.videoName = dbRow[12];
row.videoId = dbRow[13];
row.location = dbRow[4];
row.displayName = dbRow[3];
row.userId = dbRow[5];
break;
case 'Event':
row.data = dbRow[16];
break;
case 'External':
row.message = dbRow[17];
row.displayName = dbRow[3];
row.userId = dbRow[5];
row.location = dbRow[4];
break;
case 'StringLoad':
case 'ImageLoad':
row.resourceUrl = dbRow[14];
row.location = dbRow[4];
break;
}
gamelogDatabase.push(row);
},
`SELECT ${baseColumns} FROM (${selects.join(' UNION ALL ')}) ORDER BY created_at DESC, id DESC LIMIT @limit`,
args
);
return gamelogDatabase;
},
/**
* Lookup the game log database for a specific search term
* @param {string} search The search term
* @param {Array} filters The filters to apply
* @param {Array} [vipList] The list of VIP users
* @returns {Promise<any[]>} The game log data
*/
async searchGameLogDatabase(
search,
filters,
vipList,
maxEntries = dbVars.searchTableSize
) {
if (search.startsWith('wrld_') || search.startsWith('grp_')) {
return this.getGameLogByLocation(search, filters);
}
let vipQuery = '';
const vipArgs = {};
if (vipList.length > 0) {
const vipPlaceholders = [];
vipList.forEach((vip, i) => {
const key = `@vip_${i}`;
vipArgs[key] = vip;
vipPlaceholders.push(key);
});
vipQuery = `AND user_id IN (${vipPlaceholders.join(', ')})`;
}
let location = true;
let onplayerjoined = true;
let onplayerleft = true;
let portalspawn = true;
let msgevent = true;
let external = true;
let videoplay = true;
let resourceload_string = true;
let resourceload_image = true;
if (filters.length > 0) {
location = false;
onplayerjoined = false;
onplayerleft = false;
portalspawn = false;
msgevent = false;
external = false;
videoplay = false;
resourceload_string = false;
resourceload_image = false;
filters.forEach((filter) => {
switch (filter) {
case 'Location':
location = true;
break;
case 'OnPlayerJoined':
onplayerjoined = true;
break;
case 'OnPlayerLeft':
onplayerleft = true;
break;
case 'PortalSpawn':
portalspawn = true;
break;
case 'Event':
msgevent = true;
break;
case 'External':
external = true;
break;
case 'VideoPlay':
videoplay = true;
break;
case 'StringLoad':
resourceload_string = true;
break;
case 'ImageLoad':
resourceload_image = true;
break;
}
});
}
const searchLike = `%${search}%`;
const selects = [];
const baseColumns = [
'id',
'created_at',
'type',
'display_name',
'location',
'user_id',
'time',
'world_id',
'world_name',
'group_name',
'instance_id',
'video_url',
'video_name',
'video_id',
'resource_url',
'resource_type',
'data',
'message'
].join(', ');
if (location) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'Location' AS type, NULL AS display_name, location, NULL AS user_id, time, world_id, world_name, group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_location WHERE (world_name LIKE @searchLike OR group_name LIKE @searchLike) ORDER BY id DESC LIMIT @perTable)`
);
}
if (onplayerjoined || onplayerleft) {
let query = '';
if (!onplayerjoined || !onplayerleft) {
if (onplayerjoined) {
query = "AND type = 'OnPlayerJoined'";
} else if (onplayerleft) {
query = "AND type = 'OnPlayerLeft'";
}
}
selects.push(
`SELECT * FROM (SELECT id, created_at, type, display_name, location, user_id, time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_join_leave WHERE (display_name LIKE @searchLike AND user_id != '${dbVars.userId}') ${vipQuery} ${query} ORDER BY id DESC LIMIT @perTable)`
);
}
if (portalspawn) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'PortalSpawn' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, world_name, NULL AS group_name, instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_portal_spawn WHERE (display_name LIKE @searchLike OR world_name LIKE @searchLike) ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (msgevent) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'Event' AS type, NULL AS display_name, NULL AS location, NULL AS user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, data, NULL AS message FROM gamelog_event WHERE data LIKE @searchLike ORDER BY id DESC LIMIT @perTable)`
);
}
if (external) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'External' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, message FROM gamelog_external WHERE (display_name LIKE @searchLike OR message LIKE @searchLike) ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (videoplay) {
selects.push(
`SELECT * FROM (SELECT id, created_at, 'VideoPlay' AS type, display_name, location, user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, video_url, video_name, video_id, NULL AS resource_url, NULL AS resource_type, NULL AS data, NULL AS message FROM gamelog_video_play WHERE (video_url LIKE @searchLike OR video_name LIKE @searchLike OR display_name LIKE @searchLike) ${vipQuery} ORDER BY id DESC LIMIT @perTable)`
);
}
if (resourceload_string || resourceload_image) {
let checkString = '';
let checkImage = '';
if (!resourceload_string) {
checkString = `AND resource_type != 'StringLoad'`;
}
if (!resourceload_image) {
checkString = `AND resource_type != 'ImageLoad'`;
}
selects.push(
`SELECT * FROM (SELECT id, created_at, resource_type AS type, NULL AS display_name, location, NULL AS user_id, NULL AS time, NULL AS world_id, NULL AS world_name, NULL AS group_name, NULL AS instance_id, NULL AS video_url, NULL AS video_name, NULL AS video_id, resource_url, resource_type, NULL AS data, NULL AS message FROM gamelog_resource_load WHERE resource_url LIKE @searchLike ${checkString} ${checkImage} ORDER BY id DESC LIMIT @perTable)`
);
}
if (selects.length === 0) {
return [];
}
const gamelogDatabase = [];
const args = {
'@searchLike': searchLike,
'@limit': maxEntries,
'@perTable': maxEntries,
...vipArgs
};
await sqliteService.execute(
(dbRow) => {
const type = dbRow[2];
const row = {
rowId: dbRow[0],
created_at: dbRow[1],
type
};
switch (type) {
case 'Location':
row.location = dbRow[4];
row.worldId = dbRow[7];
row.worldName = dbRow[8];
row.time = dbRow[6];
row.groupName = dbRow[9];
break;
case 'OnPlayerJoined':
case 'OnPlayerLeft':
row.displayName = dbRow[3];
row.location = dbRow[4];
row.userId = dbRow[5];
row.time = dbRow[6];
break;
case 'PortalSpawn':
row.displayName = dbRow[3];
row.location = dbRow[4];
row.userId = dbRow[5];
row.instanceId = dbRow[10];
row.worldName = dbRow[8];
break;
case 'VideoPlay':
row.videoUrl = dbRow[11];
row.videoName = dbRow[12];
row.videoId = dbRow[13];
row.location = dbRow[4];
row.displayName = dbRow[3];
row.userId = dbRow[5];
break;
case 'Event':
row.data = dbRow[16];
break;
case 'External':
row.message = dbRow[17];
row.displayName = dbRow[3];
row.userId = dbRow[5];
row.location = dbRow[4];
break;
case 'StringLoad':
case 'ImageLoad':
row.resourceUrl = dbRow[14];
row.location = dbRow[4];
break;
}
gamelogDatabase.push(row);
},
`SELECT ${baseColumns} FROM (${selects.join(' UNION ALL ')}) ORDER BY created_at DESC, id DESC LIMIT @limit`,
args
);
return gamelogDatabase;
},
async getLastDateGameLogDatabase() {
var gamelogDatabase = [];
var date = new Date().toJSON();
var dateOffset = new Date(Date.now() - 86400000).toJSON(); // 24 hours
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_location ORDER BY id DESC LIMIT 1');
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_join_leave ORDER BY id DESC LIMIT 1');
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_portal_spawn ORDER BY id DESC LIMIT 1');
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_event ORDER BY id DESC LIMIT 1');
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_video_play ORDER BY id DESC LIMIT 1');
await sqliteService.execute((dbRow) => {
gamelogDatabase.push(dbRow[0]);
}, 'SELECT created_at FROM gamelog_resource_load ORDER BY id DESC LIMIT 1');
if (gamelogDatabase.length > 0) {
gamelogDatabase.sort();
var newDate = gamelogDatabase[gamelogDatabase.length - 1];
if (newDate > dateOffset && newDate < date) {
date = newDate;
}
}
return date;
},
async getGameLogWorldNameByWorldId(worldId) {
var worldName = '';
await sqliteService.execute(
(dbRow) => {
worldName = dbRow[0];
},
'SELECT world_name FROM gamelog_location WHERE world_id = @worldId ORDER BY id DESC LIMIT 1',
{
'@worldId': worldId
}
);
return worldName;
},
async getPreviousInstancesByUserId(input) {
var groupingTimeTolerance = 1 * 60 * 60 * 1000; // 1 hour
var data = new Set();
var currentGroup;
var prevEvent;
await sqliteService.execute(
(dbRow) => {
var [
created_at_iso,
created_at_ts,
location,
time,
worldName,
groupName,
eventId,
eventType
] = dbRow;
if (
!currentGroup ||
currentGroup.location !== location ||
(created_at_ts - currentGroup.last_ts >
groupingTimeTolerance && // groups multiple OnPlayerJoined and OnPlayerLeft together if they are within time tolerance limit
!(
prevEvent === 'OnPlayerJoined' &&
eventType === 'OnPlayerLeft'
)) // allows OnPlayerLeft to connect with nearby OnPlayerJoined
) {
currentGroup = {
created_at: created_at_iso,
location,
time,
worldName,
groupName,
events: [eventId],
last_ts: created_at_ts
};
data.add(currentGroup);
} else {
currentGroup.time += time;
currentGroup.last_ts = created_at_ts;
currentGroup.events.push(eventId);
}
prevEvent = eventType;
},
`
WITH grouped_locations AS (
SELECT DISTINCT location, world_name, group_name
FROM gamelog_location
)
SELECT gamelog_join_leave.created_at, strftime('%s', gamelog_join_leave.created_at) * 1000 created_at_ts, gamelog_join_leave.location, gamelog_join_leave.time, grouped_locations.world_name, grouped_locations.group_name, gamelog_join_leave.id, gamelog_join_leave.type
FROM gamelog_join_leave
INNER JOIN grouped_locations ON gamelog_join_leave.location = grouped_locations.location
WHERE user_id = @userId OR display_name = @displayName
ORDER BY gamelog_join_leave.id ASC`,
{
'@userId': input.id,
'@displayName': input.displayName
}
);
return data;
},
async getPreviousInstancesByWorldId(input) {
var data = new Map();
await sqliteService.execute(
(dbRow) => {
var time = 0;
if (dbRow[2]) {
time = dbRow[2];
}
var ref = data.get(dbRow[1]);
if (typeof ref !== 'undefined') {
time += ref.time;
}
var row = {
created_at: dbRow[0],
location: dbRow[1],
time,
worldName: dbRow[3],
groupName: dbRow[4]
};
data.set(row.location, row);
},
`SELECT created_at, location, time, world_name, group_name
FROM gamelog_location
WHERE world_id = @worldId
ORDER BY id DESC`,
{
'@worldId': input.id
}
);
return data;
},
async getPlayersFromInstance(location) {
var players = new Map();
await sqliteService.execute(
(dbRow) => {
var time = 0;
var count = 0;
var created_at = dbRow[0];
if (dbRow[3]) {
time = dbRow[3];
}
var ref = players.get(dbRow[1]);
if (typeof ref !== 'undefined') {
time += ref.time;
count = ref.count;
created_at = ref.created_at;
}
if (dbRow[4] === 'OnPlayerJoined') {
count++;
}
var row = {
created_at,
displayName: dbRow[1],
userId: dbRow[2],
time,
count
};
players.set(row.displayName, row);
},
`SELECT created_at, display_name, user_id, time, type FROM gamelog_join_leave WHERE location = @location`,
{
'@location': location
}
);
return players;
},
async getPreviousDisplayNamesByUserId(ref) {
var data = new Map();
await sqliteService.execute(
(dbRow) => {
var row = {
created_at: dbRow[0],
displayName: dbRow[1]
};
if (ref.displayName !== row.displayName) {
data.set(row.displayName, row.created_at);
}
},
`SELECT created_at, display_name
FROM gamelog_join_leave
WHERE user_id = @userId
ORDER BY id DESC`,
{
'@userId': ref.id
}
);
return data;
},
async getGameLogInstancesTime() {
var instances = new Map();
await sqliteService.execute((dbRow) => {
var time = 0;
var location = dbRow[0];
if (dbRow[1]) {
time = dbRow[1];
}
var ref = instances.get(location);
if (typeof ref !== 'undefined') {
time += ref;
}
instances.set(location, time);
}, 'SELECT location, time FROM gamelog_location');
return instances;
},
async getUserIdFromDisplayName(displayName) {
var userId = '';
await sqliteService.execute(
(row) => {
userId = row[0];
},
`SELECT user_id FROM gamelog_join_leave WHERE display_name = @displayName AND user_id != '' ORDER BY id DESC LIMIT 1`,
{
'@displayName': displayName
}
);
return userId;
},
/**
*
* @param {string} startDate: utc string of startOfDay
* @param {string} endDate: utc string endOfDay
* @returns
*/
async getInstanceActivity(startDate, endDate) {
const currentUserData = [];
const detailData = new Map();
await sqliteService.execute(
(row) => {
const rowData = {
id: row[0],
created_at: row[1],
type: row[2],
display_name: row[3],
location: row[4],
user_id: row[5],
time: row[6]
};
// skip dirty data
if (!rowData.location || rowData.location === 'traveling') {
return;
}
if (rowData.user_id === dbVars.userId) {
currentUserData.push(rowData);
}
const instanceData = detailData.get(rowData.location);
detailData.set(rowData.location, [
...(instanceData || []),
rowData
]);
},
`SELECT
*
FROM
gamelog_join_leave
WHERE type = 'OnPlayerLeft'
AND (
strftime('%Y-%m-%dT%H:%M:%SZ', created_at, '-' || (time * 1.0 / 1000) || ' seconds') BETWEEN @utc_start_date AND @utc_end_date
OR created_at BETWEEN @utc_start_date AND @utc_end_date
);`,
{
'@utc_start_date': startDate,
'@utc_end_date': endDate
}
);
return { currentUserData, detailData };
},
/**
* Get the All Date of Instance Activity for the current user
* @returns {Promise<string[]>}
*/
async getDateOfInstanceActivity() {
let result = [];
await sqliteService.execute(
(row) => {
result.push(row[0]);
},
`SELECT created_at
FROM gamelog_join_leave
WHERE user_id = @userId`,
{
'@userId': dbVars.userId
}
);
return result;
},
async getInstanceJoinHistory() {
var oneWeekAgo = new Date(Date.now() - 604800000).toJSON();
var instances = new Map();
await sqliteService.execute(
(row) => {
if (!instances.has(row[1])) {
var epoch = new Date(row[0]).getTime();
instances.set(row[1], epoch);
}
},
`SELECT created_at, location FROM gamelog_join_leave WHERE user_id = @userId AND created_at > @created_at ORDER BY created_at DESC`,
{
'@userId': dbVars.userId,
'@created_at': oneWeekAgo
}
);
return instances;
},
deleteGameLogInstanceByInstanceId(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_location WHERE location = @location`,
{
'@location': input.location
}
);
},
deleteGameLogInstance(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_join_leave WHERE (user_id = @user_id OR display_name = @displayName) AND (location = @location) AND (id in (${input.events.join(',')}))`,
{
'@user_id': input.id,
'@displayName': input.displayName,
'@location': input.location
}
);
},
deleteGameLogEntry(input) {
switch (input.type) {
case 'VideoPlay':
this.deleteGameLogVideoPlay(input);
break;
case 'Event':
this.deleteGameLogEvent(input);
break;
case 'External':
this.deleteGameLogExternal(input);
break;
case 'StringLoad':
case 'ImageLoad':
this.deleteGameLogResourceLoad(input);
break;
}
},
deleteGameLogVideoPlay(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_video_play WHERE created_at = @created_at AND video_url = @video_url AND location = @location`,
{
'@created_at': input.created_at,
'@video_url': input.videoUrl,
'@location': input.location
}
);
},
deleteGameLogEvent(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_event WHERE created_at = @created_at AND data = @data`,
{
'@created_at': input.created_at,
'@data': input.data
}
);
},
deleteGameLogExternal(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_external WHERE created_at = @created_at AND message = @message`,
{
'@created_at': input.created_at,
'@message': input.message
}
);
},
deleteGameLogResourceLoad(input) {
sqliteService.executeNonQuery(
`DELETE FROM gamelog_resource_load WHERE created_at = @created_at AND resource_url = @resource_url AND location = @location`,
{
'@created_at': input.created_at,
'@resource_url': input.resourceUrl,
'@type': input.type,
'@location': input.location
}
);
}
};
export { gameLog };