0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 09:50:43 -05:00
server/project/src/generators/WeatherGenerator.ts

188 lines
7.6 KiB
TypeScript
Raw Normal View History

import { ApplicationContext } from "@spt/context/ApplicationContext";
import { WeatherHelper } from "@spt/helpers/WeatherHelper";
import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper";
import { IWeather, IWeatherData } from "@spt/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { Season } from "@spt/models/enums/Season";
import { WindDirection } from "@spt/models/enums/WindDirection";
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { RandomUtil } from "@spt/utils/RandomUtil";
import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe";
2023-03-03 15:23:46 +00:00
@injectable()
export class WeatherGenerator {
2023-03-03 15:23:46 +00:00
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("WeatherHelper") protected weatherHelper: WeatherHelper,
@inject("PrimaryLogger") protected logger: ILogger,
2023-03-03 15:23:46 +00:00
@inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil,
2024-04-03 19:48:45 +01:00
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
2023-03-03 15:23:46 +00:00
@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
*/
public calculateGameTime(data: IWeatherData): IWeatherData {
2023-03-03 15:23:46 +00:00
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.season = this.seasonalEventService.getActiveWeatherSeason();
2024-04-03 19:48:45 +01:00
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 {
const clientAcceleratedDate = this.weatherHelper.getInRaidTime();
2023-03-03 15:23:46 +00:00
return this.getBSGFormattedTime(clientAcceleratedDate);
}
/**
* 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 {
2023-03-03 15:23:46 +00:00
return this.timeUtil.formatTime(date).replace("-", ":").replace("-", ":");
}
/**
* Return randomised Weather data with help of config/weather.json
* @param currentSeason the currently active season
* @param timestamp OPTIONAL what timestamp to generate the weather data at, defaults to now when not supplied
2023-03-03 15:23:46 +00:00
* @returns Randomised weather data
*/
public generateWeather(currentSeason: Season, timestamp?: number): IWeather {
const clouds = this.getWeightedClouds();
// Force rain to off if no clouds
const rain = clouds <= 2 ? 1 : this.getWeightedRain();
2023-03-03 15:23:46 +00:00
const result: IWeather = {
cloud: clouds,
2023-03-03 15:23:46 +00:00
wind_speed: this.getWeightedWindSpeed(),
wind_direction: this.getWeightedWindDirection(),
wind_gustiness: this.getRandomFloat("windGustiness"),
rain: rain,
rain_intensity: rain > 1 ? this.getRandomFloat("rainIntensity") : 0,
2023-03-03 15:23:46 +00:00
fog: this.getWeightedFog(),
temp: 0, // TODO - lower value at night / take into account season
2023-03-03 15:23:46 +00:00
pressure: this.getRandomFloat("pressure"),
time: "",
date: "",
timestamp: 0, // Added below
sptInRaidTimestamp: 0, // Added below
2023-03-03 15:23:46 +00:00
};
this.setCurrentDateTime(result, timestamp);
2023-03-03 15:23:46 +00:00
result.temp = this.getRaidTemperature(currentSeason, result.sptInRaidTimestamp);
2023-03-03 15:23:46 +00:00
return result;
}
2024-10-17 14:14:17 +01:00
/**
* Choose a temprature for the raid based on time of day and current season
* @param currentSeason What season tarkov is currently in
* @param inRaidTimestamp What time is the raid running at
* @returns Timestamp
*/
protected getRaidTemperature(currentSeason: Season, inRaidTimestamp: number): number {
// Convert timestamp to date so we can get current hour and check if its day or night
const currentRaidTime = new Date(inRaidTimestamp);
const seasonDayNightTempValues = this.weatherConfig.weather.temp[currentSeason];
2024-10-17 14:14:17 +01:00
const minMax = this.weatherHelper.isHourAtNightTime(currentRaidTime.getHours())
? seasonDayNightTempValues.night
: seasonDayNightTempValues.day;
return Number.parseFloat(this.randomUtil.getFloat(minMax.min, minMax.max).toPrecision(2));
}
2023-03-03 15:23:46 +00:00
/**
* Set IWeather date/time/timestamp values to now
* @param weather Object to update
* @param timestamp OPTIONAL, define timestamp used
2023-03-03 15:23:46 +00:00
*/
protected setCurrentDateTime(weather: IWeather, timestamp?: number): void {
const inRaidTime = this.weatherHelper.getInRaidTime(timestamp);
const normalTime = this.getBSGFormattedTime(inRaidTime);
const formattedDate = this.timeUtil.formatDate(inRaidTime);
const datetimeBsgFormat = `${formattedDate} ${normalTime}`;
2023-03-03 15:23:46 +00:00
weather.timestamp = Math.floor(timestamp ? timestamp : inRaidTime.getTime() / 1000); // matches weather.date
2023-03-03 15:23:46 +00:00
weather.date = formattedDate; // matches weather.timestamp
weather.time = datetimeBsgFormat; // matches weather.timestamp
weather.sptInRaidTimestamp = inRaidTime.getTime();
2023-03-03 15:23:46 +00:00
}
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;
}
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 {
2024-03-30 14:25:46 -04:00
return Number.parseFloat(
this.randomUtil
.getFloat(this.weatherConfig.weather[node].min, this.weatherConfig.weather[node].max)
2023-11-15 20:35:05 -05:00
.toPrecision(3),
);
2023-03-03 15:23:46 +00:00
}
2023-11-15 20:35:05 -05:00
}