Merge pull request 'fix some issues' (#22) from v2 into main

Reviewed-on: alekswilc/simrail-logs#22
This commit is contained in:
Aleksander Wilczyński 2024-08-18 04:46:45 +02:00
commit 42d46e32b3
Signed by: gitea
GPG Key ID: CECFC30736A3D1C8
2 changed files with 150 additions and 109 deletions

View File

@ -2,15 +2,38 @@ import { IPlayerPayload, IPlayerStatsPayload } from '../types/player.js';
const STEAM_API_KEY = process.env.STEAM_APIKEY;
const fetchFuckingSteamApi = (url: string) => {
let retries = 0;
return new Promise((res, rej) => {
const req = () => {
if (retries > 5) throw new Error('request failed to api steam');
fetch(url, { signal: AbortSignal.timeout(10000) }).then(x => x.json())
.then(x => res(x))
.catch(() => {
console.log('STEAM request failed! ', url.replace(STEAM_API_KEY!, '[XXX]'), retries)
retries++;
setTimeout(() => req(), 500);
})
}
req();
})
}
export class PlayerUtil {
public static async getPlayer(steamId: number | string) {
const data = (await fetch(`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=${STEAM_API_KEY}&format=json&steamids=${steamId}`).then(x => x.json())) as IPlayerPayload;
const data = await fetchFuckingSteamApi(`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=${STEAM_API_KEY}&format=json&steamids=${steamId}`) as IPlayerPayload;
if (!data.response.players) return;
return data.response.players[0];
}
public static async getPlayerStats(steamId: string) {
const data = (await fetch(`http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=1422130&key=${STEAM_API_KEY}&steamid=${steamId}`).then(x => x.json())) as IPlayerStatsPayload;
const data = await fetchFuckingSteamApi(`http://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v0002/?appid=1422130&key=${STEAM_API_KEY}&steamid=${steamId}`) as IPlayerStatsPayload;
if (!data.playerstats?.stats) return;
return data.playerstats;
}

View File

@ -78,122 +78,140 @@ export class SimrailClient extends EventEmitter {
this.trainsOccupied = (await redis.json.get('trains_occupied') as unknown as SimrailClient['trainsOccupied']);
}
private async processStation(server: Server, stations: ApiResponse<Station>) {
if (stations.result) {
if (!this.stations[server.ServerCode]) this.stations[server.ServerCode] = [];
if (!this.stationsOccupied[server.ServerCode]) this.stationsOccupied[server.ServerCode] = {};
if (!this.stations[server.ServerCode].length) {
this.stations[server.ServerCode] = stations.data;
redis.json.set('stations', '$', this.stations);
}
stations.data.forEach(async (x) => {
const data = this.stations[server.ServerCode].find(y => y.Name === x.Name);
if (!data) return;
if (data.DispatchedBy[0]?.SteamId !== x.DispatchedBy[0]?.SteamId) {
if (!data.DispatchedBy[0]?.SteamId) {
// join
const date = new Date();
const player = await PlayerUtil.getPlayer(x.DispatchedBy[0]?.SteamId);
this.emit(SimrailClientEvents.StationJoined, server, x, player);
this.stationsOccupied[server.ServerCode][data.Prefix] = {
SteamId: x.DispatchedBy[0]?.SteamId,
JoinedAt: date.getTime()
};
return;
}
// leave
const player = await PlayerUtil.getPlayer(data.DispatchedBy[0]?.SteamId)
this.emit(SimrailClientEvents.StationLeft, server, x, player, this.stationsOccupied[server.ServerCode][data.Prefix]?.JoinedAt);
delete this.stationsOccupied[server.ServerCode][data.Prefix];
}
})
redis.json.set('stations_occupied', '$', this.stationsOccupied);
this.stations[server.ServerCode] = stations.data;
redis.json.set('stations', '$', this.stations);
}
}
private async processTrain(server: Server, trains: ApiResponse<Train>) {
if (trains.result) {
if (!this.trains[server.ServerCode]) this.trains[server.ServerCode] = [];
if (!this.trainsOccupied[server.ServerCode]) this.trainsOccupied[server.ServerCode] = {};
if (!this.trains[server.ServerCode].length) {
this.trains[server.ServerCode] = trains.data;
redis.json.set('trains', '$', this.trains);
return;
}
trains.data.forEach(async (x) => {
const data = this.trains[server.ServerCode].find(y => y.id === x.id);
if (!data) return;
if (data.TrainData.ControlledBySteamID !== x.TrainData.ControlledBySteamID) {
if (!data.TrainData.ControlledBySteamID) {
if (!x.TrainData.ControlledBySteamID) return;
// join
const date = new Date();
const player = await PlayerUtil.getPlayer(x.TrainData.ControlledBySteamID!);
const playerStats = await PlayerUtil.getPlayerStats(x.TrainData.ControlledBySteamID!);
this.emit(SimrailClientEvents.TrainJoined, server, x, player, playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value);
this.trainsOccupied[server.ServerCode][x.TrainNoLocal] = {
SteamId: x.TrainData.ControlledBySteamID!,
JoinedAt: date.getTime(),
StartPlayerDistance: playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value!,
StartPlayerPoints: playerStats?.stats.find(x => x.name === "SCORE")?.value!,
};
return;
}
if (!data.TrainData.ControlledBySteamID) return;
const date = new Date();
const player = await PlayerUtil.getPlayer(data.TrainData.ControlledBySteamID!);
const playerId = data.TrainData.ControlledBySteamID!;
const trainOccupied = this.trainsOccupied[server.ServerCode][data.TrainNoLocal] && JSON.parse(JSON.stringify(this.trainsOccupied[server.ServerCode][data.TrainNoLocal])) || null;
setTimeout(() => {
PlayerUtil.getPlayerStats(playerId).then(playerStats => {
const oldKm = trainOccupied?.StartPlayerDistance ?? 0;
const distance = oldKm ? (playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value ?? 0) - oldKm : 0;
const oldPoints = trainOccupied?.StartPlayerPoints ?? 0;
const points = oldPoints ? (playerStats?.stats.find(x => x.name === 'SCORE')?.value ?? 0) - oldPoints : 0;
this.emit(SimrailClientEvents.TrainLeft, server, data, player, trainOccupied?.JoinedAt, date.getTime(), points, distance, x.Vehicles[0]);
})
}, 80_000)
delete this.trainsOccupied[server.ServerCode][data.TrainNoLocal];
}
})
this.trains[server.ServerCode] = trains.data;
redis.json.set('trains', '$', this.trains);
redis.json.set('trains_occupied', '$', this.trainsOccupied);
}
}
private async update() {
const servers = (await fetch('https://panel.simrail.eu:8084/servers-open').then(x => x.json()).catch(x => ({data: [], result: false})) as ApiResponse<Server>)
const servers = (await fetch('https://panel.simrail.eu:8084/servers-open').then(x => x.json()).catch(x => ({ data: [], result: false })) as ApiResponse<Server>)
.data?.filter(x => x.ServerName.includes('Polski')) ?? []; // no plans to support other servers
if (!servers.length) console.log('SimrailAPI is down')
// TODO: maybe node:worker_threads?
// TODO: check performance
servers.forEach(async (server) => {
const stations = (await fetch('https://panel.simrail.eu:8084/stations-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Station>;
const trains = (await fetch('https://panel.simrail.eu:8084/trains-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Train>;
if (stations.result) {
if (!this.stations[server.ServerCode]) this.stations[server.ServerCode] = [];
if (!this.stationsOccupied[server.ServerCode]) this.stationsOccupied[server.ServerCode] = {};
const stations = (await fetch('https://panel.simrail.eu:8084/stations-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Station>;
const trains = (await fetch('https://panel.simrail.eu:8084/trains-open?serverCode=' + server.ServerCode).then(x => x.json()).catch(() => ({ result: false }))) as ApiResponse<Train>;
if (!this.stations[server.ServerCode].length) {
this.stations[server.ServerCode] = stations.data;
redis.json.set('stations', '$', this.stations);
return;
}
this.processStation(server, stations);
this.processTrain(server, trains);
stations.data.forEach(async (x) => {
const data = this.stations[server.ServerCode].find(y => y.Name === x.Name);
if (!data) return;
if (data.DispatchedBy[0]?.SteamId !== x.DispatchedBy[0]?.SteamId) {
if (!data.DispatchedBy[0]?.SteamId) {
// join
const date = new Date();
const player = await PlayerUtil.getPlayer(x.DispatchedBy[0]?.SteamId);
this.emit(SimrailClientEvents.StationJoined, server, x, player);
this.stationsOccupied[server.ServerCode][data.Prefix] = {
SteamId: x.DispatchedBy[0]?.SteamId,
JoinedAt: date.getTime()
};
return;
}
// leave
const player = await PlayerUtil.getPlayer(data.DispatchedBy[0]?.SteamId)
this.emit(SimrailClientEvents.StationLeft, server, x, player, this.stationsOccupied[server.ServerCode][data.Prefix]?.JoinedAt);
delete this.stationsOccupied[server.ServerCode][data.Prefix];
}
})
redis.json.set('stations_occupied', '$', this.stationsOccupied);
this.stations[server.ServerCode] = stations.data;
redis.json.set('stations', '$', this.stations);
}
if (trains.result) {
if (!this.trains[server.ServerCode]) this.trains[server.ServerCode] = [];
if (!this.trainsOccupied[server.ServerCode]) this.trainsOccupied[server.ServerCode] = {};
if (!this.trains[server.ServerCode].length) {
this.trains[server.ServerCode] = trains.data;
redis.json.set('trains', '$', this.trains);
return;
}
trains.data.forEach(async (x) => {
const data = this.trains[server.ServerCode].find(y => y.id === x.id);
if (!data) return;
if (data.TrainData.ControlledBySteamID !== x.TrainData.ControlledBySteamID) {
if (!data.TrainData.ControlledBySteamID) {
if (!x.TrainData.ControlledBySteamID) return;
// join
const date = new Date();
const player = await PlayerUtil.getPlayer(x.TrainData.ControlledBySteamID!);
const playerStats = await PlayerUtil.getPlayerStats(x.TrainData.ControlledBySteamID!);
this.emit(SimrailClientEvents.TrainJoined, server, x, player, playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value);
this.trainsOccupied[server.ServerCode][x.TrainNoLocal] = {
SteamId: x.TrainData.ControlledBySteamID!,
JoinedAt: date.getTime(),
StartPlayerDistance: playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value!,
StartPlayerPoints: playerStats?.stats.find(x => x.name === "SCORE")?.value!,
};
return;
}
if (!data.TrainData.ControlledBySteamID) return;
const date = new Date();
const player = await PlayerUtil.getPlayer(data.TrainData.ControlledBySteamID!);
const playerId = data.TrainData.ControlledBySteamID!;
const trainOccupied = this.trainsOccupied[server.ServerCode][data.TrainNoLocal];
setTimeout(async () => {
const playerStats = await PlayerUtil.getPlayerStats(playerId);
const oldKm = trainOccupied?.StartPlayerDistance ?? 0;
const distance = oldKm ? (playerStats?.stats.find(x => x.name === 'DISTANCE_M')?.value ?? 0) - oldKm : 0;
const oldPoints = trainOccupied?.StartPlayerPoints ?? 0;
const points = oldPoints ? (playerStats?.stats.find(x => x.name === 'SCORE')?.value ?? 0) - oldPoints : 0;
this.emit(SimrailClientEvents.TrainLeft, server, data, player, trainOccupied?.JoinedAt, date.getTime(), points, distance, x.Vehicles[0]);
}, 60000)
delete this.trainsOccupied[server.ServerCode][data.TrainNoLocal];
}
})
this.trains[server.ServerCode] = trains.data;
redis.json.set('trains', '$', this.trains);
redis.json.set('trains_occupied', '$', this.trainsOccupied);
}
});