This commit is contained in:
Minecon724 2024-09-28 13:07:30 +02:00
parent 163c9bd71d
commit 18b7df1650
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
12 changed files with 276 additions and 81 deletions

View file

@ -33,7 +33,7 @@
<dependency> <dependency>
<groupId>eu.m724</groupId> <groupId>eu.m724</groupId>
<artifactId>wtapi</artifactId> <artifactId>wtapi</artifactId>
<version>0.8.0</version> <version>0.8.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>eu.m724</groupId> <groupId>eu.m724</groupId>
@ -46,6 +46,13 @@
<version>3.0.3</version> <version>3.0.3</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>

View file

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module version="4"> <module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$" dumb="true">
<sourceFolder url="file://$MODULE_DIR$/src/main/test" isTestSource="true" />
</content>
</component>
<component name="FacetManager"> <component name="FacetManager">
<facet type="minecraft" name="Minecraft"> <facet type="minecraft" name="Minecraft">
<configuration> <configuration>

View file

@ -135,7 +135,7 @@ public class RealWeatherPlugin extends JavaPlugin {
getCommand("rwadmin").setExecutor(new AdminCommand(updater)); getCommand("rwadmin").setExecutor(new AdminCommand(updater));
getCommand("geo").setExecutor(new GeoCommand()); getCommand("geo").setExecutor(new GeoCommand());
if (GlobalConstants.timeConfig.enabled) if (GlobalConstants.timeConfig.enabled())
getCommand("localtime").setExecutor(new LocalTimeCommand()); getCommand("localtime").setExecutor(new LocalTimeCommand());
if (GlobalConstants.weatherConfig.enabled) if (GlobalConstants.weatherConfig.enabled)
@ -149,7 +149,7 @@ public class RealWeatherPlugin extends JavaPlugin {
GlobalConstants.thunderConfig.enabled ? GlobalConstants.thunderConfig.provider : "off" GlobalConstants.thunderConfig.enabled ? GlobalConstants.thunderConfig.provider : "off"
)); ));
metrics.addCustomChart(new SimplePie("real_time", () -> metrics.addCustomChart(new SimplePie("real_time", () ->
GlobalConstants.timeConfig.enabled ? "on" : "off" GlobalConstants.timeConfig.enabled() ? "on" : "off"
)); ));
DebugLogger.info("ended loading", 1); DebugLogger.info("ended loading", 1);

View file

@ -63,13 +63,13 @@ public class AdminCommand implements CommandExecutor {
} }
componentBuilder.append("\nTime: ").color(ChatColor.GOLD); componentBuilder.append("\nTime: ").color(ChatColor.GOLD);
componentBuilder.append(timeConfig.enabled ? enabledComponent : disabledComponent); componentBuilder.append(timeConfig.enabled() ? enabledComponent : disabledComponent);
if (timeConfig.enabled) { if (timeConfig.enabled()) {
componentBuilder.append(" Scale: ").color(ChatColor.GOLD); componentBuilder.append(" Scale: ").color(ChatColor.GOLD);
componentBuilder.append(Double.toString(timeConfig.scale) + "\n").color(ChatColor.AQUA); componentBuilder.append(Double.toString(timeConfig.scale()) + "\n").color(ChatColor.AQUA);
long worldTime = timeConfig.calculateWorldTimeSeconds(); long worldTime = 0; // TODO timeConfig.calculateWorldTimeSeconds();
Duration worldTimeDuration = Duration.ofSeconds(worldTime); Duration worldTimeDuration = Duration.ofSeconds(worldTime);
String worldTimeFormatted = String.format("%d:%02d:%02d\n", String worldTimeFormatted = String.format("%d:%02d:%02d\n",
@ -81,7 +81,7 @@ public class AdminCommand implements CommandExecutor {
componentBuilder.append(worldTimeFormatted).color(ChatColor.AQUA); componentBuilder.append(worldTimeFormatted).color(ChatColor.AQUA);
componentBuilder.append(" Dynamic: ").color(ChatColor.GOLD); componentBuilder.append(" Dynamic: ").color(ChatColor.GOLD);
componentBuilder.append(timeConfig.dynamic ? enabledComponent : disabledComponent); componentBuilder.append(timeConfig.dynamic() ? enabledComponent : disabledComponent);
} }
componentBuilder.append("\nThunder: ").color(ChatColor.GOLD); componentBuilder.append("\nThunder: ").color(ChatColor.GOLD);

View file

@ -23,7 +23,7 @@ public class LocalTimeCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Player player = sender instanceof Player ? (Player) sender : null; Player player = sender instanceof Player ? (Player) sender : null;
long worldTime = timeConfig.calculateWorldTimeSeconds(); long worldTime = 0; // TODO timeConfig.calculateWorldTimeSeconds();
Duration worldTimeDuration = Duration.ofSeconds(worldTime); Duration worldTimeDuration = Duration.ofSeconds(worldTime);
String worldTimeFormatted = String.format("%d:%02d:%02d", String worldTimeFormatted = String.format("%d:%02d:%02d",
@ -37,10 +37,10 @@ public class LocalTimeCommand implements CommandExecutor {
sender.spigot().sendMessage(component); sender.spigot().sendMessage(component);
if (timeConfig.dynamic && player != null && player.hasPermission("realweather.dynamic")) { if (timeConfig.dynamic() && player != null && player.hasPermission("realweather.dynamic")) {
Coordinates coordinates = mapper.locationToCoordinates(player.getLocation()); Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
long offsetTime = timeConfig.calculateTimeOffsetSeconds(coordinates.longitude); long offsetTime = 0; // TODO timeConfig.calculateTimeOffsetSeconds(coordinates.longitude);
boolean negative = offsetTime < 0; boolean negative = offsetTime < 0;
Duration localTimeDuration = worldTimeDuration.plus(offsetTime, ChronoUnit.SECONDS); Duration localTimeDuration = worldTimeDuration.plus(offsetTime, ChronoUnit.SECONDS);

View file

@ -10,12 +10,14 @@ import eu.m724.realweather.mapper.Mapper;
import eu.m724.wtapi.object.Coordinates; import eu.m724.wtapi.object.Coordinates;
public class AsyncPlayerTimeTask extends BukkitRunnable { public class AsyncPlayerTimeTask extends BukkitRunnable {
private Server server = GlobalConstants.getPlugin().getServer(); private final Server server = GlobalConstants.getPlugin().getServer();
private Mapper mapper = GlobalConstants.getMapper(); private final Mapper mapper = GlobalConstants.getMapper();
private TimeConfig timeConfig; private final TimeConverter timeConverter;
private final TimeConfig timeConfig;
AsyncPlayerTimeTask(TimeConfig timeConfig) { AsyncPlayerTimeTask(TimeConverter timeConverter, TimeConfig timeConfig) {
this.timeConverter = timeConverter;
this.timeConfig = timeConfig; this.timeConfig = timeConfig;
} }
@ -26,7 +28,20 @@ public class AsyncPlayerTimeTask extends BukkitRunnable {
if (!mapper.getWorlds().contains(player.getWorld())) continue; if (!mapper.getWorlds().contains(player.getWorld())) continue;
Coordinates coordinates = mapper.locationToCoordinates(player.getLocation()); Coordinates coordinates = mapper.locationToCoordinates(player.getLocation());
long offsetTicks = timeConfig.calculateTimeOffsetTicks(coordinates.longitude);
long time = System.currentTimeMillis();
time = timeConverter.scale(time);
if (timeConfig.twilight()) {
time = timeConverter.offsetTwilight(time, mapper.getPoint());
}
time = timeConverter.calculateZoneOffset(coordinates.longitude);
long ticks = timeConverter.millisToTicks(time);
long offsetTicks = timeConverter.calculateZoneOffset(coordinates.longitude);
offsetTicks = timeConverter.millisToTicks(offsetTicks);
player.setPlayerTime(Math.floorMod(offsetTicks, 24000), true); player.setPlayerTime(Math.floorMod(offsetTicks, 24000), true);
DebugLogger.info("Time for %s: %d", 2, player.getName(), offsetTicks); DebugLogger.info("Time for %s: %d", 2, player.getName(), offsetTicks);

View file

@ -1,5 +1,6 @@
package eu.m724.realweather.time; package eu.m724.realweather.time;
import eu.m724.wtapi.object.Coordinates;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import eu.m724.realweather.DebugLogger; import eu.m724.realweather.DebugLogger;
@ -7,17 +8,26 @@ import eu.m724.realweather.GlobalConstants;
import eu.m724.realweather.mapper.Mapper; import eu.m724.realweather.mapper.Mapper;
public class SyncTimeUpdateTask extends BukkitRunnable { public class SyncTimeUpdateTask extends BukkitRunnable {
private TimeConfig timeConfig; private final Mapper mapper = GlobalConstants.getMapper();
private Mapper mapper = GlobalConstants.getMapper();
SyncTimeUpdateTask(TimeConfig timeConfig) { private final TimeConverter timeConverter;
private final TimeConfig timeConfig;
SyncTimeUpdateTask(TimeConverter timeConverter, TimeConfig timeConfig) {
this.timeConverter = timeConverter;
this.timeConfig = timeConfig; this.timeConfig = timeConfig;
} }
@Override @Override
public void run() { public void run() {
// TODO double? long time = System.currentTimeMillis();
long ticks = timeConfig.calculateWorldTimeTicks(); time = timeConverter.scale(time);
if (timeConfig.twilight()) {
time = timeConverter.offsetTwilight(time, mapper.getPoint());
}
long ticks = timeConverter.millisToTicks(time);
DebugLogger.info("Updating time: %d", 2, ticks); DebugLogger.info("Updating time: %d", 2, ticks);
mapper.getWorlds().forEach(world -> world.setFullTime(ticks)); mapper.getWorlds().forEach(world -> world.setFullTime(ticks));

View file

@ -2,54 +2,23 @@ package eu.m724.realweather.time;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
public class TimeConfig { import java.util.UUID;
public boolean enabled;
// state is per player public record TimeConfig(
public boolean dynamic; boolean enabled,
public boolean twilight;
// x day cycles in 1 irl day
public double scale;
boolean dynamic,
boolean twilight,
double scale
) {
public static TimeConfig fromConfiguration(ConfigurationSection configuration) { public static TimeConfig fromConfiguration(ConfigurationSection configuration) {
TimeConfig timeConfig = new TimeConfig(); boolean enabled = configuration.getBoolean("enabled");
timeConfig.enabled = configuration.getBoolean("enabled"); boolean dynamic = configuration.getBoolean("dynamic");
boolean twilight = configuration.getBoolean("twilight");
double scale = configuration.getDouble("scale");
timeConfig.dynamic = configuration.getBoolean("dynamic"); return new TimeConfig(enabled, dynamic, twilight, scale);
timeConfig.twilight = configuration.getBoolean("twilight");
timeConfig.scale = configuration.getDouble("scale");
return timeConfig;
}
public long calculateWorldTimeSeconds() {
long now = (long) (System.currentTimeMillis() * scale / 1000) % 86400;
return now;
}
public long calculateWorldTimeTicks() {
long now = calculateWorldTimeSeconds();
long ticks = Math.floorMod(now / 72 * 20 - 6000, 24000);
return ticks;
}
/**
* this method is mostly for info
* @param longitude
* @return
*/
public long calculateTimeOffsetSeconds(double longitude) {
long offset = (long) ((longitude / 15) * 3600 * scale);
return offset;
}
public long calculateTimeOffsetTicks(double longitude) {
long offsetTicks = (long) ((longitude / 15) * 1000 * scale);
return offsetTicks;
} }
} }

View file

@ -0,0 +1,148 @@
package eu.m724.realweather.time;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import eu.m724.wtapi.object.TwilightCalendar;
import eu.m724.wtapi.provider.twilight.TwilightTimeCache;
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import eu.m724.wtapi.provider.twilight.impl.approximate.ApproximateTwilightTimeCache;
import eu.m724.wtapi.provider.twilight.impl.approximate.ApproximateTwilightTimeProvider;
import org.checkerframework.checker.units.qual.A;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;
public class TimeConverter {
public final double scale;
// TODO make this configurable
private final ApproximateTwilightTimeProvider provider = new ApproximateTwilightTimeProvider();
public TimeConverter(double scale) {
this.scale = scale;
}
/**
* Divides time by predefined scale<br>
* ...slowing it down
*
* @param time unix milliseconds
* @return scaled unix milliseconds
*/
public long scale(long time) {
return (long) (time / scale);
}
/**
* Converts unix timestamp to in game ticks
*
* @param time unix millis
* @return in-game ticks
*/
public long millisToTicks(long time) {
return Math.floorMod(time / 3600 - 6000, 24000);
}
/**
* Calculates how often (or how rarely) we can update time<br>
* That is, to not do stuff when we don't have to<br>
* In ticks, because it's used with Bukkit scheduler
*
* @return update period IN TICKS
*/
public long calculateUpdatePeriod() {
return (long) (72 / scale);
}
/**
* Calculates time offset (in millis) for specified longitude
*
* @param longitude the longitude
* @return milliseconds of offset from the center of the world (0)
*/
public long calculateZoneOffset(double longitude) {
return (long) (longitude * 3600000);
}
// TODO optimize
public long offsetTwilight(long unixTimestampMs, Coordinates coordinates) {
// Constants
final long MILLIS_PER_DAY = 86400000;
final long SCALED_SUNRISE_MS = 6 * 3600000; // 06:00 in milliseconds
final long SCALED_SUNSET_MS = 18 * 3600000; // 18:00 in milliseconds
final long SCALED_DAY_LENGTH_MS = SCALED_SUNSET_MS - SCALED_SUNRISE_MS;
// Convert Unix timestamp to Instant and LocalDate
Instant instant = Instant.ofEpochMilli(unixTimestampMs);
LocalDate date = LocalDate.ofInstant(instant, ZoneOffset.UTC);
// Get twilight information for the date
TwilightCalendar calendar = provider.getCalendar(date);
Twilight twilight = calendar.twilight(coordinates);
System.out.println("Twilight converted date: " + twilight.date());
System.out.println("Sunrise: " + twilight.sunrise());
System.out.println("Sunset: " + twilight.sunset());
if (twilight.isPolarDay()) {
long daytime = unixTimestampMs % 86400000;
if (daytime < 21600000) {
unixTimestampMs += 43200000;
} else if (daytime > 64800000) {
unixTimestampMs += 43200000;
}
System.out.println("Polar day");
return unixTimestampMs;
} else if (twilight.isPolarNight()) {
long daytime = unixTimestampMs % 86400000;
if (daytime > 21600000 && daytime < 64800000) {
unixTimestampMs += 43200000;
}
System.out.println("Polar night");
return unixTimestampMs;
}
long sunriseMs = twilight.sunrise().toInstant(ZoneOffset.UTC).toEpochMilli();
long sunsetMs = twilight.sunset().toInstant(ZoneOffset.UTC).toEpochMilli();
// Handle edge cases
if (unixTimestampMs == sunriseMs) return SCALED_SUNRISE_MS;
if (unixTimestampMs == sunsetMs) return SCALED_SUNSET_MS;
long startTimeMs, endTimeMs, scaledStartMs;
if (unixTimestampMs >= sunriseMs && unixTimestampMs < sunsetMs) {
// Daytime
startTimeMs = sunriseMs;
endTimeMs = sunsetMs;
scaledStartMs = SCALED_SUNRISE_MS;
} else {
// Nighttime
if (unixTimestampMs >= sunsetMs) {
// After sunset
startTimeMs = sunsetMs;
endTimeMs = calendar.tomorrow().twilight(coordinates).sunrise().toInstant(ZoneOffset.UTC).toEpochMilli();
} else {
// Before sunrise
Twilight yesterdayTwilight = calendar.yesterday().twilight(coordinates);
startTimeMs = yesterdayTwilight.sunset().toInstant(ZoneOffset.UTC).toEpochMilli();
endTimeMs = sunriseMs;
}
scaledStartMs = SCALED_SUNSET_MS;
}
double durationMs = endTimeMs - startTimeMs;
double progress = (unixTimestampMs - startTimeMs) / durationMs;
long scaledOffsetMs = scaledStartMs + (long) (progress * SCALED_DAY_LENGTH_MS);
// Ensure the result is within the same day (0 to MILLIS_PER_DAY - 1)
return scaledOffsetMs % MILLIS_PER_DAY;
}
}

View file

@ -7,31 +7,35 @@ import eu.m724.realweather.DebugLogger;
import eu.m724.realweather.GlobalConstants; import eu.m724.realweather.GlobalConstants;
import eu.m724.realweather.mapper.Mapper; import eu.m724.realweather.mapper.Mapper;
import java.time.LocalTime;
public class TimeMaster { public class TimeMaster {
private TimeConfig timeConfig; private final Mapper mapper = GlobalConstants.getMapper();
private Mapper mapper = GlobalConstants.getMapper(); private final Plugin plugin = GlobalConstants.getPlugin();
private Plugin plugin = GlobalConstants.getPlugin();
private final TimeConfig timeConfig;
private final TimeConverter timeConverter;
public TimeMaster(TimeConfig timeConfig) { public TimeMaster(TimeConfig timeConfig) {
this.timeConfig = timeConfig; this.timeConfig = timeConfig;
this.timeConverter = new TimeConverter(timeConfig.scale());
} }
public void init() { public void init() {
if (!timeConfig.enabled) if (!timeConfig.enabled())
return; return;
long period = (long) (72 / timeConfig.scale); long period = timeConverter.calculateUpdatePeriod();
if (timeConfig.scale * Math.floor(period) != 72.0) { if (timeConfig.scale() * period != 72.0) {
// TODO log this properly // TODO log this properly
DebugLogger.info("Warning: RealTime scale is not optimal. Time will be out of sync.", 0); DebugLogger.info("Warning: RealTime scale is not optimal. Time will be out of sync.", 0);
} }
new SyncTimeUpdateTask(timeConfig).runTaskTimer(plugin, 0, period); new SyncTimeUpdateTask(timeConverter, timeConfig).runTaskTimer(plugin, 0, period);
if (timeConfig.dynamic) if (timeConfig.dynamic())
new AsyncPlayerTimeTask(timeConfig).runTaskTimerAsynchronously(plugin, 0, period); new AsyncPlayerTimeTask(timeConverter, timeConfig).runTaskTimerAsynchronously(plugin, 0, period);
// TODO maybe use a different period? // TODO maybe use a different period?
// TODO also make it on player join // TODO also make it on player join

View file

@ -22,5 +22,4 @@ twilight: true
# x in game day cycles in 1 irl day cycle # x in game day cycles in 1 irl day cycle
# Time will no longer be in sync # Time will no longer be in sync
# Can be decimal
scale: 1.0 scale: 1.0

View file

@ -0,0 +1,38 @@
import eu.m724.realweather.time.TimeConverter;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import org.junit.Test;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
public class TimeConverterTest {
@Test
public void testTimeConverter() {
TimeConverter timeConverter = new TimeConverter(2.0);
System.out.println("Scale: " + timeConverter.scale);
long time = LocalDateTime.now().withHour(12).toInstant(ZoneOffset.UTC).toEpochMilli();
System.out.println("Time: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneOffset.UTC));
time = timeConverter.scale(time);
System.out.println("Time scaled: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneOffset.UTC));
System.out.println("Those times are UTC so add 7 hours");
time = timeConverter.offsetTwilight(time, new Coordinates(42, 69)); // TODO change numbers before committing
time = timeConverter.millisToTicks(time);
System.out.println("Day ticks: " + time);
System.out.println();
System.out.println("Scale: " + timeConverter.scale);
time = LocalDateTime.now().withHour(12).toInstant(ZoneOffset.UTC).toEpochMilli();
System.out.println("Time: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneOffset.UTC));
time = timeConverter.scale(time);
System.out.println("Time scaled: " + LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneOffset.UTC));
System.out.println("Those times are UTC so add 7 hours");
time = timeConverter.offsetTwilight(time, new Coordinates(70, 0)); // TODO change numbers before committing
time = timeConverter.millisToTicks(time);
System.out.println("Day ticks: " + time);
}
}