Server/project/src/generators/WeatherGenerator.ts

184 lines
6.3 KiB
TypeScript
Raw Normal View History

2023-03-03 15:23:46 +00:00
import { inject, injectable } from "tsyringe";
import { ApplicationContext } from "@spt-aki/context/ApplicationContext";
import { ContextVariableType } from "@spt-aki/context/ContextVariableType";
import { WeightedRandomHelper } from "@spt-aki/helpers/WeightedRandomHelper";
import { IWeather, IWeatherData } from "@spt-aki/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { WindDirection } from "@spt-aki/models/enums/WindDirection";
import { IWeatherConfig } from "@spt-aki/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil";
2023-03-03 15:23:46 +00:00
@injectable()
export class WeatherGenerator
{
protected weatherConfig: IWeatherConfig;
Make accelerated time calculations independent of client (!205) Changes server accelerated time calculation to be fully independent of client calculations. Local testing over most of a day showed time acceleration continuing through relogging as a client, and calculated times being synced to about +- 2 seconds between server and client with no drift. ----------- In #202 I referenced the client side formula for accelerate time: `In Raid Time = Today's Date + Location Time + Time Since Client Connection * Acceleration` At the time I didn't know where Location Time was set and conservatively tried to match the server calculations to the client. Since then I've confirmed that it is set after calling `client/game/start` and holds the accelerated server timestamp from that call. With this in mind, I'm more confident changing the server calculations and here we are. Previously each time you started your client, the accelerated time would start counting from your irl time at launch. This change moves that to the server, so you could leave your server running to have a more live-like experience where you won't be sure of the in raid accelerated time until you log in. Added benefit of significantly simplifying the `getInRaidTime()` code. Future work could be done to add save/load support to the server's timestamp to further emulate the live experience where timers won't reset to your irl time unless you wipe the data. I'd personally lean towards saving it at a server level, not a profile level, to allow multiple profiles to share a single 'wipe'. ----------- Co-authored-by: OkaMoez <43766412+OkaMoez@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/205 Co-authored-by: OkaMoez <okamoez@noreply.dev.sp-tarkov.com> Co-committed-by: OkaMoez <okamoez@noreply.dev.sp-tarkov.com>
2024-01-23 10:13:53 +00:00
// Note: If this value gets save/load support, raid time could be tracked across server restarts
// Currently it will set the In Raid time to your current real time on server launch
private serverStartTimestampMS = Date.now();
2023-03-03 15:23:46 +00:00
constructor(
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
@inject("WinstonLogger") protected logger: ILogger,
@inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("ApplicationContext") protected applicationContext: ApplicationContext,
2023-11-15 20:35:05 -05:00
@inject("ConfigServer") protected configServer: ConfigServer,
2023-03-03 15:23:46 +00:00
)
{
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
}
2023-07-25 14:04:21 +01:00
/**
* Get current + raid datetime and format into correct BSG format and return
* @param data Weather data
* @returns IWeatherData
*/
2023-03-03 15:23:46 +00:00
public calculateGameTime(data: IWeatherData): IWeatherData
{
const computedDate = new Date();
const formattedDate = this.timeUtil.formatDate(computedDate);
data.date = formattedDate;
data.time = this.getBsgFormattedInRaidTime();
2023-03-03 15:23:46 +00:00
data.acceleration = this.weatherConfig.acceleration;
data.winterEventEnabled = this.weatherConfig.forceWinterEvent;
2023-03-03 15:23:46 +00:00
return data;
}
/**
* Get server uptime seconds multiplied by a multiplier and add to current time as seconds
* Format to BSGs requirements
* @param currentDate current date
* @returns formatted time
*/
protected getBsgFormattedInRaidTime(): string
2023-03-03 15:23:46 +00:00
{
const clientAcceleratedDate = this.getInRaidTime();
2023-03-03 15:23:46 +00:00
return this.getBSGFormattedTime(clientAcceleratedDate);
}
/**
* Get the current in-raid time
* @param currentDate (new Date())
2023-11-15 20:35:05 -05:00
* @returns Date object of current in-raid time
2023-03-03 15:23:46 +00:00
*/
public getInRaidTime(): Date
2023-03-03 15:23:46 +00:00
{
// tarkov time = (real time * 7 % 24 hr) + 3 hour
const russiaOffset = (this.timeUtil.getHoursAsSeconds(3)) * 1000;
2024-02-23 16:10:18 +00:00
return new Date(
(russiaOffset + (new Date().getTime() * this.weatherConfig.acceleration))
% (this.timeUtil.getHoursAsSeconds(24) * 1000),
);
2023-03-03 15:23:46 +00:00
}
/**
* Get current time formatted to fit BSGs requirement
* @param date date to format into bsg style
2023-07-25 14:04:21 +01:00
* @returns Time formatted in BSG format
2023-03-03 15:23:46 +00:00
*/
protected getBSGFormattedTime(date: Date): string
{
return this.timeUtil.formatTime(date).replace("-", ":").replace("-", ":");
}
/**
* Return randomised Weather data with help of config/weather.json
* @returns Randomised weather data
*/
public generateWeather(): IWeather
{
const rain = this.getWeightedRain();
const result: IWeather = {
cloud: this.getWeightedClouds(),
2023-03-03 15:23:46 +00:00
wind_speed: this.getWeightedWindSpeed(),
wind_direction: this.getWeightedWindDirection(),
wind_gustiness: this.getRandomFloat("windGustiness"),
rain: rain,
2023-11-15 20:35:05 -05:00
rain_intensity: (rain > 1) ? this.getRandomFloat("rainIntensity") : 0,
2023-03-03 15:23:46 +00:00
fog: this.getWeightedFog(),
temp: this.getRandomFloat("temp"),
pressure: this.getRandomFloat("pressure"),
time: "",
date: "",
2023-11-15 20:35:05 -05:00
timestamp: 0,
2023-03-03 15:23:46 +00:00
};
this.setCurrentDateTime(result);
return result;
}
/**
* Set IWeather date/time/timestamp values to now
* @param weather Object to update
*/
protected setCurrentDateTime(weather: IWeather): void
{
const currentDate = this.getInRaidTime();
2023-03-03 15:23:46 +00:00
const normalTime = this.getBSGFormattedTime(currentDate);
const formattedDate = this.timeUtil.formatDate(currentDate);
const datetime = `${formattedDate} ${normalTime}`;
weather.timestamp = Math.floor(currentDate.getTime() / 1000); // matches weather.date
weather.date = formattedDate; // matches weather.timestamp
weather.time = datetime; // matches weather.timestamp
}
protected getWeightedWindDirection(): WindDirection
{
2023-11-15 20:35:05 -05:00
return this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.windDirection.values,
this.weatherConfig.weather.windDirection.weights,
).item;
2023-03-03 15:23:46 +00:00
}
protected getWeightedClouds(): number
{
2023-11-15 20:35:05 -05:00
return this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.clouds.values,
this.weatherConfig.weather.clouds.weights,
).item;
}
2023-03-03 15:23:46 +00:00
protected getWeightedWindSpeed(): number
{
2023-11-15 20:35:05 -05:00
return this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.windSpeed.values,
this.weatherConfig.weather.windSpeed.weights,
).item;
2023-03-03 15:23:46 +00:00
}
protected getWeightedFog(): number
{
2023-11-15 20:35:05 -05:00
return this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.fog.values,
this.weatherConfig.weather.fog.weights,
).item;
2023-03-03 15:23:46 +00:00
}
protected getWeightedRain(): number
{
2023-11-15 20:35:05 -05:00
return this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.rain.values,
this.weatherConfig.weather.rain.weights,
).item;
2023-03-03 15:23:46 +00:00
}
protected getRandomFloat(node: string): number
{
2023-11-15 20:35:05 -05:00
return parseFloat(
this.randomUtil.getFloat(this.weatherConfig.weather[node].min, this.weatherConfig.weather[node].max)
.toPrecision(3),
);
2023-03-03 15:23:46 +00:00
}
2023-11-15 20:35:05 -05:00
}