diff --git a/pom.xml b/pom.xml
index ba3d0a4..c0d7d0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
eu.m724
wtapi
- 0.8.0
+ 0.8.3
eu.m724
@@ -46,6 +46,13 @@
3.0.3
compile
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
diff --git a/realweather.iml b/realweather.iml
index a589521..7f2ab86 100644
--- a/realweather.iml
+++ b/realweather.iml
@@ -1,5 +1,10 @@
+
+
+
+
+
diff --git a/src/main/java/eu/m724/realweather/RealWeatherPlugin.java b/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
index f21ab4d..f6e0764 100644
--- a/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
+++ b/src/main/java/eu/m724/realweather/RealWeatherPlugin.java
@@ -135,7 +135,7 @@ public class RealWeatherPlugin extends JavaPlugin {
getCommand("rwadmin").setExecutor(new AdminCommand(updater));
getCommand("geo").setExecutor(new GeoCommand());
- if (GlobalConstants.timeConfig.enabled)
+ if (GlobalConstants.timeConfig.enabled())
getCommand("localtime").setExecutor(new LocalTimeCommand());
if (GlobalConstants.weatherConfig.enabled)
@@ -149,7 +149,7 @@ public class RealWeatherPlugin extends JavaPlugin {
GlobalConstants.thunderConfig.enabled ? GlobalConstants.thunderConfig.provider : "off"
));
metrics.addCustomChart(new SimplePie("real_time", () ->
- GlobalConstants.timeConfig.enabled ? "on" : "off"
+ GlobalConstants.timeConfig.enabled() ? "on" : "off"
));
DebugLogger.info("ended loading", 1);
diff --git a/src/main/java/eu/m724/realweather/commands/AdminCommand.java b/src/main/java/eu/m724/realweather/commands/AdminCommand.java
index a945dea..75648ef 100644
--- a/src/main/java/eu/m724/realweather/commands/AdminCommand.java
+++ b/src/main/java/eu/m724/realweather/commands/AdminCommand.java
@@ -63,13 +63,13 @@ public class AdminCommand implements CommandExecutor {
}
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(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);
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(" Dynamic: ").color(ChatColor.GOLD);
- componentBuilder.append(timeConfig.dynamic ? enabledComponent : disabledComponent);
+ componentBuilder.append(timeConfig.dynamic() ? enabledComponent : disabledComponent);
}
componentBuilder.append("\nThunder: ").color(ChatColor.GOLD);
diff --git a/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java b/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java
index 358ad27..f0050bd 100644
--- a/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java
+++ b/src/main/java/eu/m724/realweather/commands/LocalTimeCommand.java
@@ -23,7 +23,7 @@ public class LocalTimeCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Player player = sender instanceof Player ? (Player) sender : null;
- long worldTime = timeConfig.calculateWorldTimeSeconds();
+ long worldTime = 0; // TODO timeConfig.calculateWorldTimeSeconds();
Duration worldTimeDuration = Duration.ofSeconds(worldTime);
String worldTimeFormatted = String.format("%d:%02d:%02d",
@@ -37,10 +37,10 @@ public class LocalTimeCommand implements CommandExecutor {
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());
- long offsetTime = timeConfig.calculateTimeOffsetSeconds(coordinates.longitude);
+ long offsetTime = 0; // TODO timeConfig.calculateTimeOffsetSeconds(coordinates.longitude);
boolean negative = offsetTime < 0;
Duration localTimeDuration = worldTimeDuration.plus(offsetTime, ChronoUnit.SECONDS);
diff --git a/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java b/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
index 740f2c4..43b2f88 100644
--- a/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
+++ b/src/main/java/eu/m724/realweather/time/AsyncPlayerTimeTask.java
@@ -10,12 +10,14 @@ import eu.m724.realweather.mapper.Mapper;
import eu.m724.wtapi.object.Coordinates;
public class AsyncPlayerTimeTask extends BukkitRunnable {
- private Server server = GlobalConstants.getPlugin().getServer();
- private Mapper mapper = GlobalConstants.getMapper();
+ private final Server server = GlobalConstants.getPlugin().getServer();
+ 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;
}
@@ -24,9 +26,22 @@ public class AsyncPlayerTimeTask extends BukkitRunnable {
for (Player player : server.getOnlinePlayers()) {
if (!player.hasPermission("realweather.dynamic")) continue;
if (!mapper.getWorlds().contains(player.getWorld())) continue;
-
+
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);
DebugLogger.info("Time for %s: %d", 2, player.getName(), offsetTicks);
diff --git a/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java b/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
index da46348..f9f7cc0 100644
--- a/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
+++ b/src/main/java/eu/m724/realweather/time/SyncTimeUpdateTask.java
@@ -1,5 +1,6 @@
package eu.m724.realweather.time;
+import eu.m724.wtapi.object.Coordinates;
import org.bukkit.scheduler.BukkitRunnable;
import eu.m724.realweather.DebugLogger;
@@ -7,17 +8,26 @@ import eu.m724.realweather.GlobalConstants;
import eu.m724.realweather.mapper.Mapper;
public class SyncTimeUpdateTask extends BukkitRunnable {
- private TimeConfig timeConfig;
- private Mapper mapper = GlobalConstants.getMapper();
-
- SyncTimeUpdateTask(TimeConfig timeConfig) {
+ private final Mapper mapper = GlobalConstants.getMapper();
+
+ private final TimeConverter timeConverter;
+ private final TimeConfig timeConfig;
+
+ SyncTimeUpdateTask(TimeConverter timeConverter, TimeConfig timeConfig) {
+ this.timeConverter = timeConverter;
this.timeConfig = timeConfig;
}
@Override
public void run() {
- // TODO double?
- long ticks = timeConfig.calculateWorldTimeTicks();
+ long time = System.currentTimeMillis();
+ 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);
mapper.getWorlds().forEach(world -> world.setFullTime(ticks));
diff --git a/src/main/java/eu/m724/realweather/time/TimeConfig.java b/src/main/java/eu/m724/realweather/time/TimeConfig.java
index af8eaaa..49c1e64 100644
--- a/src/main/java/eu/m724/realweather/time/TimeConfig.java
+++ b/src/main/java/eu/m724/realweather/time/TimeConfig.java
@@ -2,54 +2,23 @@ package eu.m724.realweather.time;
import org.bukkit.configuration.ConfigurationSection;
-public class TimeConfig {
- public boolean enabled;
-
- // state is per player
- public boolean dynamic;
- public boolean twilight;
- // x day cycles in 1 irl day
- public double scale;
-
+import java.util.UUID;
+
+public record TimeConfig(
+ boolean enabled,
+
+ boolean dynamic,
+ boolean twilight,
+
+ double scale
+) {
public static TimeConfig fromConfiguration(ConfigurationSection configuration) {
- TimeConfig timeConfig = new TimeConfig();
+ boolean enabled = configuration.getBoolean("enabled");
+
+ boolean dynamic = configuration.getBoolean("dynamic");
+ boolean twilight = configuration.getBoolean("twilight");
+ double scale = configuration.getDouble("scale");
- timeConfig.enabled = configuration.getBoolean("enabled");
-
- timeConfig.dynamic = configuration.getBoolean("dynamic");
- 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;
+ return new TimeConfig(enabled, dynamic, twilight, scale);
}
}
diff --git a/src/main/java/eu/m724/realweather/time/TimeConverter.java b/src/main/java/eu/m724/realweather/time/TimeConverter.java
new file mode 100644
index 0000000..17d6c5f
--- /dev/null
+++ b/src/main/java/eu/m724/realweather/time/TimeConverter.java
@@ -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
+ * ...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
+ * That is, to not do stuff when we don't have to
+ * 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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/realweather/time/TimeMaster.java b/src/main/java/eu/m724/realweather/time/TimeMaster.java
index 44bcd08..edb5107 100644
--- a/src/main/java/eu/m724/realweather/time/TimeMaster.java
+++ b/src/main/java/eu/m724/realweather/time/TimeMaster.java
@@ -7,31 +7,35 @@ import eu.m724.realweather.DebugLogger;
import eu.m724.realweather.GlobalConstants;
import eu.m724.realweather.mapper.Mapper;
+import java.time.LocalTime;
+
public class TimeMaster {
- private TimeConfig timeConfig;
- private Mapper mapper = GlobalConstants.getMapper();
- private Plugin plugin = GlobalConstants.getPlugin();
+ private final Mapper mapper = GlobalConstants.getMapper();
+ private final Plugin plugin = GlobalConstants.getPlugin();
+
+ private final TimeConfig timeConfig;
+ private final TimeConverter timeConverter;
public TimeMaster(TimeConfig timeConfig) {
this.timeConfig = timeConfig;
+ this.timeConverter = new TimeConverter(timeConfig.scale());
}
-
public void init() {
- if (!timeConfig.enabled)
+ if (!timeConfig.enabled())
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
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)
- new AsyncPlayerTimeTask(timeConfig).runTaskTimerAsynchronously(plugin, 0, period);
+ if (timeConfig.dynamic())
+ new AsyncPlayerTimeTask(timeConverter, timeConfig).runTaskTimerAsynchronously(plugin, 0, period);
// TODO maybe use a different period?
// TODO also make it on player join
diff --git a/src/main/resources/modules/time.yml b/src/main/resources/modules/time.yml
index 8840092..54ebb61 100644
--- a/src/main/resources/modules/time.yml
+++ b/src/main/resources/modules/time.yml
@@ -22,5 +22,4 @@ twilight: true
# x in game day cycles in 1 irl day cycle
# Time will no longer be in sync
-# Can be decimal
scale: 1.0
\ No newline at end of file
diff --git a/src/test/java/TimeConverterTest.java b/src/test/java/TimeConverterTest.java
new file mode 100644
index 0000000..698fd53
--- /dev/null
+++ b/src/test/java/TimeConverterTest.java
@@ -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);
+ }
+}