From 41b236e3034ed537ed58eb9d4940b1f9f6090f4e Mon Sep 17 00:00:00 2001 From: Song Minjae Date: Wed, 5 Apr 2017 02:52:00 +0900 Subject: [PATCH] new WorldTime based on TIME_T --- .gitignore | 1 + .idea/libraries/lib.xml | 14 - src/net/torvald/terrarum/console/SetTime.kt | 6 +- .../torvald/terrarum/console/SetTimeDelta.kt | 5 +- .../torvald/terrarum/gameworld/GameWorld.kt | 6 +- .../torvald/terrarum/gameworld/WorldTime.kt | 199 ++++--- .../terrarum/gameworld/WorldTime_old.kt | 199 +++++++ .../terrarum/ui/BasicDebugInfoWindow.kt | 2 +- .../virtualcomputer/luaapi/Filesystem.kt | 516 ------------------ .../torvald/terrarum/weather/WeatherMixer.kt | 4 +- 10 files changed, 304 insertions(+), 648 deletions(-) create mode 100644 src/net/torvald/terrarum/gameworld/WorldTime_old.kt delete mode 100644 src/net/torvald/terrarum/virtualcomputer/luaapi/Filesystem.kt diff --git a/.gitignore b/.gitignore index 46e5bd3eb..83c9257db 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin/* hs_err_pid* Thumbs.db .DS_Store +/.idea/workspace.xml diff --git a/.idea/libraries/lib.xml b/.idea/libraries/lib.xml index fee5556f2..b5c503a41 100644 --- a/.idea/libraries/lib.xml +++ b/.idea/libraries/lib.xml @@ -1,26 +1,12 @@ - - - - - - - - - - - - - - diff --git a/src/net/torvald/terrarum/console/SetTime.kt b/src/net/torvald/terrarum/console/SetTime.kt index 94623e133..d742cc98c 100644 --- a/src/net/torvald/terrarum/console/SetTime.kt +++ b/src/net/torvald/terrarum/console/SetTime.kt @@ -11,10 +11,10 @@ internal object SetTime : ConsoleCommand { if (args.size == 2) { val timeToSet = WorldTime.parseTime(args[1]) - Terrarum.ingame!!.world.time.setTime(timeToSet) + Terrarum.ingame!!.world.time.setTimeOfToday(timeToSet) - Echo("Set time to ${Terrarum.ingame!!.world.time.elapsedSeconds} " + - "(${Terrarum.ingame!!.world.time.hours}h${formatMin(Terrarum.ingame!!.world.time.minutes)})") + Echo("Set time to ${Terrarum.ingame!!.world.time.todaySeconds} " + + "(${Terrarum.ingame!!.world.time.hours}h${formatMin(Terrarum.ingame!!.world.time.minutes)})") } else { printUsage() diff --git a/src/net/torvald/terrarum/console/SetTimeDelta.kt b/src/net/torvald/terrarum/console/SetTimeDelta.kt index a2599b598..8008c7759 100644 --- a/src/net/torvald/terrarum/console/SetTimeDelta.kt +++ b/src/net/torvald/terrarum/console/SetTimeDelta.kt @@ -11,10 +11,7 @@ internal object SetTimeDelta : ConsoleCommand { override fun execute(args: Array) { if (args.size == 2) { - if (args[1].toInt() > HARD_LIMIT) - EchoError("Delta too large -- acceptable delta is 0-60.") - - Terrarum.ingame!!.world.time.setTimeDelta(args[1].toInt()) + Terrarum.ingame!!.world.time.timeDelta = args[1].toInt() if (Terrarum.ingame!!.world.time.timeDelta == 0) Echo("時間よ止まれ!ザ・ワルド!!") else diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 866a039db..17fe7fa34 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -50,7 +50,11 @@ constructor(//properties terrainDamage = PairedMapLayer(width, height) wallDamage = PairedMapLayer(width, height) - time = WorldTime() + time = WorldTime( + 71 * WorldTime.DAY_LENGTH + + 7 * WorldTime.HOUR_SEC + + 30L * WorldTime.MINUTE_SEC + ) } /** diff --git a/src/net/torvald/terrarum/gameworld/WorldTime.kt b/src/net/torvald/terrarum/gameworld/WorldTime.kt index 6c6438bb5..98719b4ee 100644 --- a/src/net/torvald/terrarum/gameworld/WorldTime.kt +++ b/src/net/torvald/terrarum/gameworld/WorldTime.kt @@ -3,7 +3,8 @@ package net.torvald.terrarum.gameworld import net.torvald.terrarum.gameactors.GameDate /** - * The World Calendar implementation of Dwarven Calendar (we're talking about DF!) + * The World Calendar implementation of Dwarven Calendar (we're talking about DF!), + * except the year begins with Mondag instead of Sundag * * Please see: * https://en.wikipedia.org/wiki/World_Calendar @@ -12,36 +13,90 @@ import net.torvald.terrarum.gameactors.GameDate * Normal format for day is * Tysdag 12th Granite * - * And there is no AM/PM concept, 22-hour clock is forced. + * And there is no AM/PM concept, 22-hour clock is forced; no leap years. + * + * + * Calendar + * + * |Mo|Ty|Mi|To|Fr|La|Su|Ve| + * |--|--|--|--|--|--|--|--| + * | 1| 2| 3| 4| 5| 6| 7| | + * | 8| 9|10|11|12|13|14| | + * |15|16|17|18|19|20|21| | + * |22|23|24|25|26|27|28| | + * |29|30|31| 1| 2| 3| 4| | + * | 5| 6| 7| 8| 9|10|11| | + * |12|13|14|15|16|17|18| | + * |19|20|21|22|23|24|25| | + * |26|27|28|29|30| 1| 2| | + * | 3| 4| 5| 6| 7| 8| 9| | + * |10|11|12|13|14|15|16| | + * |17|18|19|20|21|22|23| | + * |24|25|26|27|28|29|30|31| + * + * + * * * Created by minjaesong on 16-01-24. */ -class WorldTime { - internal var TIME_T = 0L // TODO use it! Epoch: Year 125, 1st Granite, 0h00:00 +class WorldTime(initTime: Long = 0L) { + var TIME_T = 0L // Epoch: Year 125, 1st Opal, 0h00:00 (Mondag) // 125-01-01 + private set - internal var seconds: Int // 0 - 59 - internal var minutes: Int // 0 - 59 - internal var hours: Int // 0 - 21 + init { + TIME_T = initTime + } - // days on the year - internal var yearlyDays: Int //NOT a calendar day + val seconds: Int // 0 - 59 + get() = TIME_T.toPositiveInt() % MINUTE_SEC + val minutes: Int // 0 - 59 + get() = TIME_T.div(MINUTE_SEC).abs().toInt() % HOUR_MIN + val hours: Int // 0 - 21 + get() = TIME_T.div(HOUR_SEC).abs().toInt() % HOURS_PER_DAY - internal var days: Int // 1 - 31 - internal var months: Int // 1 - 12 - internal var years: Int // 1+ + val yearlyDays: Int // 0 - 364 + get() = (TIME_T.toPositiveInt().div(DAY_LENGTH) % YEAR_DAYS) - internal var dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7) + val days: Int // 1 - 31 + get() = quarterlyDays + 1 - + if (quarterlyMonthOffset == 0) 0 + else if (quarterlyMonthOffset == 1) 31 + else 61 + val months: Int // 1 - 12 + get() = if (yearlyDays == YEAR_DAYS - 1) 12 else + quarter * 3 + 1 + + if (quarterlyDays < 31) 0 + else if (quarterlyDays < 61) 1 + else 2 + val years: Int + get() = TIME_T.div(YEAR_DAYS * DAY_LENGTH).abs().toInt() + EPOCH_YEAR - internal var timeDelta = 1 + val quarter: Int // 0 - 3 + get() = if (yearlyDays == YEAR_DAYS - 1) 3 else yearlyDays / QUARTER_LENGTH + val quarterlyDays: Int // 0 - 90(91) + get() = if (yearlyDays == YEAR_DAYS - 1) 91 else (yearlyDays % QUARTER_LENGTH) + val quarterlyMonthOffset: Int // 0 - 2 + get() = months.minus(1) % 3 - @Transient private var realMillisec: Int + val dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7) + get() = if (yearlyDays == YEAR_DAYS - 1) 7 else yearlyDays % 7 + + var timeDelta: Int = 1 + set(value) { + field = if (value < 0) 0 else value + } + + @Transient private var realMillisec: Double = 0.0 + @Transient private val REAL_SEC_TO_GAME_SECS = 60 val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o) "Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur - , "Torsdag", "Fredag", "Laurdag", "Sundag", "Verdag" //From Norsk word 'verd' + , "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd' ) val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver") + // FIXME Next to Granite is Felsite + val MONTH_NAMES = arrayOf( "Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite", "Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone" @@ -52,8 +107,6 @@ class WorldTime { val currentTimeAsGameDate: GameDate get() = GameDate(years, yearlyDays) - @Transient val REAL_SEC_IN_MILLI = 1000 - companion object { /** Each day is 22-hour long */ val DAY_LENGTH = 79200 //must be the multiple of 3600 @@ -62,8 +115,12 @@ class WorldTime { val MINUTE_SEC: Int = 60 val HOUR_MIN: Int = 60 val GAME_MIN_TO_REAL_SEC: Float = 60f + val HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC val YEAR_DAYS: Int = 365 + val QUARTER_LENGTH = 91 // as per The World Calendar + + val EPOCH_YEAR = 125 fun parseTime(s: String): Int = if (s.length >= 4 && s.contains('h')) { @@ -78,111 +135,37 @@ class WorldTime { } } - init { - seconds = 0 - minutes = 30 - hours = 8 - yearlyDays = 73 - days = 12 - months = 3 - years = 125 - dayOfWeek = 1 // Tysdag - realMillisec = 0 - } - fun update(delta: Int) { - val oldsec = seconds - //time - realMillisec += delta * timeDelta - val newsec = Math.round(GAME_MIN_TO_REAL_SEC.toFloat() / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat()) - seconds = newsec - - if (realMillisec >= REAL_SEC_IN_MILLI) - realMillisec -= REAL_SEC_IN_MILLI - - kickVariables() + realMillisec += delta + if (realMillisec >= 1000.0 / REAL_SEC_TO_GAME_SECS) { + realMillisec -= 1000.0 / REAL_SEC_TO_GAME_SECS + TIME_T += timeDelta + } } - /** - * How much time has passed today, in seconds. - * 0 == 6 AM - * @return - */ - val elapsedSeconds: Int - get() = (HOUR_SEC * hours + MINUTE_SEC * minutes + seconds) % DAY_LENGTH - /** Sets time of this day. */ - fun setTime(t: Int) { - days += t / DAY_LENGTH - hours = t / HOUR_SEC - minutes = (t - HOUR_SEC * hours) / MINUTE_SEC - seconds = t - minutes * MINUTE_SEC - yearlyDays += t / DAY_LENGTH + val todaySeconds: Int + get() = TIME_T.toPositiveInt() % DAY_LENGTH + + fun setTimeOfToday(t: Int) { + TIME_T = TIME_T - todaySeconds + t } fun addTime(t: Int) { - setTime(elapsedSeconds + t) - } - - fun setTimeDelta(d: Int) { - timeDelta = if (d < 0) 0 else d + TIME_T += t } val dayName: String get() = DAY_NAMES[dayOfWeek] - private fun kickVariables() { - if (seconds >= MINUTE_SEC) { - seconds = 0 - minutes += 1 - } - - if (minutes >= HOUR_MIN) { - minutes = 0 - hours += 1 - } - - if (hours >= DAY_LENGTH / HOUR_SEC) { - hours = 0 - days += 1 - yearlyDays += 1 - dayOfWeek += 1 - } - - //calendar (the world calendar) - if (dayOfWeek == 7) { - dayOfWeek = 0 - } - if (months == 12 && days == 31) { - dayOfWeek = 7 - } - - if (months == 12 && days == 32) { - days = 1 - months = 1 - years++ - } - else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31) { - days = 1 - months++ - } - else if (days > 30) { - days = 1 - months++ - } - - if (months > 12) { - months = 1 - years++ - yearlyDays = 1 - } - } + private fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt() + private fun Long.abs() = Math.abs(this) /** Format: "%A %d %B %Y %X" */ - fun getFormattedTime() = "${getDayNameFull()} " + + fun getFormattedTime() = "${getDayNameShort()} " + "$days " + - "${getMonthNameFull()} " + + "${getMonthNameShort()} " + "$years " + "${String.format("%02d", hours)}:" + "${String.format("%02d", minutes)}:" + @@ -192,4 +175,6 @@ class WorldTime { fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek] fun getMonthNameFull() = MONTH_NAMES[months - 1] fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1] + + override fun toString() = getFormattedTime() } \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameworld/WorldTime_old.kt b/src/net/torvald/terrarum/gameworld/WorldTime_old.kt new file mode 100644 index 000000000..8a7a5623c --- /dev/null +++ b/src/net/torvald/terrarum/gameworld/WorldTime_old.kt @@ -0,0 +1,199 @@ +package net.torvald.terrarum.gameworld + +import net.torvald.terrarum.gameactors.GameDate + +/** + * The World Calendar implementation of Dwarven Calendar (we're talking about DF!) + * + * Please see: + * https://en.wikipedia.org/wiki/World_Calendar + * http://dwarffortresswiki.org/index.php/DF2014:Calendar + * + * Normal format for day is + * Tysdag 12th Granite + * + * And there is no AM/PM concept, 22-hour clock is forced. + * + * Created by minjaesong on 16-01-24. + */ +class YeOldeWorldTime { + internal var TIME_T = 0L // TODO use it! Epoch: Year 125, 1st Granite, 0h00:00 + + internal var seconds: Int // 0 - 59 + internal var minutes: Int // 0 - 59 + internal var hours: Int // 0 - 21 + + // days on the year + internal var yearlyDays: Int //NOT a calendar day + + internal var days: Int // 1 - 31 + internal var months: Int // 1 - 12 + internal var years: Int // 1+ + + internal var dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7) + + internal var timeDelta = 1 + + @Transient private var realMillisec: Int + + val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o) + "Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur + , "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd' + ) + val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver") + + val MONTH_NAMES = arrayOf( + "Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite", + "Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone" + ) + val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema", + "Mala", "Gale", "Lime", "Sand", "Timb", "Moon") + + val currentTimeAsGameDate: GameDate + get() = GameDate(years, yearlyDays) + + @Transient val REAL_SEC_IN_MILLI = 1000 + + companion object { + /** Each day is 22-hour long */ + val DAY_LENGTH = 79200 //must be the multiple of 3600 + + val HOUR_SEC: Int = 3600 + val MINUTE_SEC: Int = 60 + val HOUR_MIN: Int = 60 + val GAME_MIN_TO_REAL_SEC: Float = 60f + + val YEAR_DAYS: Int = 365 + + fun parseTime(s: String): Int = + if (s.length >= 4 && s.contains('h')) { + s.toLowerCase().substringBefore('h').toInt() * WorldTime.HOUR_SEC + + s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC + } + else if (s.endsWith("h", true)) { + s.toLowerCase().substring(0, s.length - 1).toInt() * WorldTime.HOUR_SEC + } + else { + s.toInt() + } + } + + init { + // The day when the new world ever is being made. + // If we use Multiverse system (which replaces Terraria's "hack" + // as a reward rather than a cheat), time of current world's time is + // copied to the new world's. (it's Multi-nation rather than Multiverse) + seconds = 0 + minutes = 30 + hours = 8 + yearlyDays = 73 + days = 12 + months = 3 + years = 125 + dayOfWeek = 1 // Tysdag + realMillisec = 0 + } + + fun update(delta: Int) { + val oldsec = seconds + + //time + realMillisec += delta * timeDelta + val newsec = Math.round(GAME_MIN_TO_REAL_SEC / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat()) + seconds = newsec + + if (realMillisec >= REAL_SEC_IN_MILLI) + realMillisec -= REAL_SEC_IN_MILLI + + kickVariables() + } + + /** + * How much time has passed today, in seconds. + * 0 == 6 AM + * @return + */ + val elapsedSeconds: Int + get() = (HOUR_SEC * hours + MINUTE_SEC * minutes + seconds) % DAY_LENGTH + + /** Sets time of this day. */ + fun setTime(t: Int) { + days += t / DAY_LENGTH + hours = t / HOUR_SEC + minutes = (t - HOUR_SEC * hours) / MINUTE_SEC + seconds = t - minutes * MINUTE_SEC + yearlyDays += t / DAY_LENGTH + } + + fun addTime(t: Int) { + setTime(elapsedSeconds + t) + } + + fun setTimeDelta(d: Int) { + timeDelta = if (d < 0) 0 else d + } + + val dayName: String + get() = DAY_NAMES[dayOfWeek] + + private fun kickVariables() { + if (seconds >= MINUTE_SEC) { + seconds = 0 + minutes += 1 + } + + if (minutes >= HOUR_MIN) { + minutes = 0 + hours += 1 + } + + if (hours >= DAY_LENGTH / HOUR_SEC) { + hours = 0 + days += 1 + yearlyDays += 1 + dayOfWeek += 1 + } + + //calendar (the world calendar) + if (dayOfWeek == 7) { + dayOfWeek = 0 + } + if (months == 12 && days == 31) { + dayOfWeek = 7 + } + + if (months == 12 && days == 32) { + days = 1 + months = 1 + years++ + } + else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31) { + days = 1 + months++ + } + else if (days > 30) { + days = 1 + months++ + } + + if (months > 12) { + months = 1 + years++ + yearlyDays = 1 + } + } + + /** Format: "%A %d %B %Y %X" */ + fun getFormattedTime() = "${getDayNameFull()} " + + "$days " + + "${getMonthNameFull()} " + + "$years " + + "${String.format("%02d", hours)}:" + + "${String.format("%02d", minutes)}:" + + "${String.format("%02d", seconds)}" + + fun getDayNameFull() = DAY_NAMES[dayOfWeek] + fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek] + fun getMonthNameFull() = MONTH_NAMES[months - 1] + fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1] +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt index 1dfd17a42..b2496e468 100644 --- a/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt +++ b/src/net/torvald/terrarum/ui/BasicDebugInfoWindow.kt @@ -127,7 +127,7 @@ class BasicDebugInfoWindow : UICanvas { printLineColumn(g, 2, 1, "VSync $ccG" + Terrarum.appgc.isVSyncRequested) printLineColumn(g, 2, 2, "Env colour temp $ccG" + FeaturesDrawer.colTemp) - printLineColumn(g, 2, 5, "Time $ccG${Terrarum.ingame!!.world.time.elapsedSeconds}" + + printLineColumn(g, 2, 5, "Time $ccG${Terrarum.ingame!!.world.time.todaySeconds.toString().padStart(5, '0')}" + " (${Terrarum.ingame!!.world.time.getFormattedTime()})") printLineColumn(g, 2, 6, "Mass $ccG${player.mass}") diff --git a/src/net/torvald/terrarum/virtualcomputer/luaapi/Filesystem.kt b/src/net/torvald/terrarum/virtualcomputer/luaapi/Filesystem.kt deleted file mode 100644 index dedb66abf..000000000 --- a/src/net/torvald/terrarum/virtualcomputer/luaapi/Filesystem.kt +++ /dev/null @@ -1,516 +0,0 @@ -package net.torvald.terrarum.virtualcomputer.luaapi - -import org.luaj.vm2.* -import org.luaj.vm2.lib.OneArgFunction -import org.luaj.vm2.lib.TwoArgFunction -import org.luaj.vm2.lib.ZeroArgFunction -import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.toHex -import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer -import net.torvald.terrarum.virtualcomputer.luaapi.Term.Companion.checkIBM437 -import java.io.* -import java.nio.file.Files -import java.nio.file.NoSuchFileException -import java.nio.file.Path -import java.nio.file.Paths -import java.util.* - -/** - * computer directory: - * .../computers/ - * media/hda/ -> .../computers// - * - * Created by minjaesong on 16-09-17. - * - * - * NOTES: - * Don't convert '\' to '/'! Rev-slash is used for escape character in sh, and we're sh-compatible! - * Use .absoluteFile whenever possible; there's fuckin oddity! (http://bugs.java.com/bugdatabase/view_bug.do;:YfiG?bug_id=4483097) - */ -internal class Filesystem(globals: Globals, computer: TerrarumComputer) { - - init { - // load things. WARNING: THIS IS MANUAL! - globals["fs"] = LuaValue.tableOf() - globals["fs"]["list"] = ListFiles(computer) // CC compliant - globals["fs"]["exists"] = FileExists(computer) // CC/OC compliant - globals["fs"]["isDir"] = IsDirectory(computer) // CC compliant - globals["fs"]["isFile"] = IsFile(computer) - globals["fs"]["isReadOnly"] = IsReadOnly(computer) // CC compliant - globals["fs"]["getSize"] = GetSize(computer) // CC compliant - globals["fs"]["mkdir"] = Mkdir(computer) - globals["fs"]["mv"] = Mv(computer) - globals["fs"]["cp"] = Cp(computer) - globals["fs"]["rm"] = Rm(computer) - globals["fs"]["concat"] = ConcatPath(computer) // OC compliant - globals["fs"]["open"] = OpenFile(computer) //CC compliant - globals["fs"]["parent"] = GetParentDir(computer) - // fs.dofile defined in BOOT - // fs.fetchText defined in ROMLIB - } - - companion object { - fun ensurePathSanity(path: LuaValue) { - if (path.checkIBM437().contains(Regex("""\.\."""))) - throw LuaError("'..' on path is not supported.") - if (!isValidFilename(path.checkIBM437())) - throw IOException("path contains invalid characters") - } - - var isCaseInsensitive: Boolean - - init { - try { - val uuid = UUID.randomUUID().toString() - val lowerCase = File(Terrarum.currentSaveDir, uuid + "oc_rox") - val upperCase = File(Terrarum.currentSaveDir, uuid + "OC_ROX") - // This should NEVER happen but could also lead to VERY weird bugs, so we - // make sure the files don't exist. - if (lowerCase.exists()) lowerCase.delete() - if (upperCase.exists()) upperCase.delete() - - lowerCase.createNewFile() - - val insensitive = upperCase.exists() - lowerCase.delete() - - isCaseInsensitive = insensitive - - println("[Filesystem] Case insensitivity: $isCaseInsensitive") - } - catch (e: IOException) { - System.err.println("[Filesystem] Couldn't determine if the file system is case sensitive, falling back to insensitive.") - e.printStackTrace(System.out) - isCaseInsensitive = true - } - } - - // Worst-case: we're on Windows or using a FAT32 partition mounted in *nix. - // Note: we allow / as the path separator and expect all \s to be converted - // accordingly before the path is passed to the file system. - private val invalidChars = Regex("""[<>:"|?*\u0000-\u001F]""") // original OC uses Set(); we use regex - - fun isValidFilename(name: String) = !name.contains(invalidChars) - - fun String.validatePath() : String { - if (!isValidFilename(this)) { - throw IOException("path contains invalid characters") - } - return this - } - - /** actual directory: /Saves//computers// - * directs media/ directory to / directory - */ - fun TerrarumComputer.getRealPath(luapath: LuaValue) : String { - // direct mounted paths to real path - val computerDir = Terrarum.currentSaveDir.absolutePath + "/computers/" - /* if not begins with "(/?)media/", direct to boot - * else, to corresponding drives - * - * List of device names (these are auto-mounted. why? primitivism :p): - * = hda - hdd: hard disks - * = fd1 - fd4: floppy drives - * = sda: whatever external drives, usually a CD - * = boot: current boot device - */ - - // remove first '/' in path - var path = luapath.checkIBM437().validatePath() - if (path.startsWith('/')) path = path.substring(1) - - val finalPath: String - - if (path.startsWith("media/")) { - val device = path.substring(6, 9) - val subPath = path.substring(9) - finalPath = computerDir + this.computerValue.getAsString("device") + subPath - } - else { - finalPath = computerDir + this.computerValue.getAsString("boot") + "/" + path - } - - // remove trailing slash - return if (finalPath.endsWith("\\") || finalPath.endsWith("/")) - finalPath.substring(0, finalPath.length - 1) - else - finalPath - } - - fun combinePath(base: String, local: String) : String { - return "$base$local".replace("//", "/") - } - } - - /** - * @param cname == UUID of the drive - * - * actual directory: /Saves//computers// - */ - class ListFiles(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - println("ListFiles: got path ${path.checkIBM437()}") - - val table = LuaTable() - val file = File(computer.getRealPath(path)).absoluteFile - try { - file.list().forEachIndexed { i, s -> table.insert(i, LuaValue.valueOf(s)) } - } - catch (e: NullPointerException) {} - return table - } - } - - /** Don't use this. Use isFile */ - class FileExists(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - return LuaValue.valueOf(Files.exists(Paths.get(computer.getRealPath(path)).toAbsolutePath())) - } - } - - class IsDirectory(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - val isDir = Files.isDirectory(Paths.get(computer.getRealPath(path)).toAbsolutePath()) - val exists = Files.exists(Paths.get(computer.getRealPath(path)).toAbsolutePath()) - - return LuaValue.valueOf(isDir || exists) - } - } - - class IsFile(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - // check if the path is file by checking: - // 1. isfile - // 2. canwrite - // 3. length - // Why? Our Java simply wants to fuck you. - - val path = Paths.get(computer.getRealPath(path)).toAbsolutePath() - var result = false - result = Files.isRegularFile(path) - - if (!result) result = Files.isWritable(path) - - if (!result) - try { result = Files.size(path) > 0 } - catch (e: NoSuchFileException) { result = false } - - return LuaValue.valueOf(result) - } - } - - class IsReadOnly(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - return LuaValue.valueOf(!Files.isWritable(Paths.get(computer.getRealPath(path)).toAbsolutePath())) - } - } - - /** we have 4GB file size limit */ - class GetSize(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - return LuaValue.valueOf(Files.size(Paths.get(computer.getRealPath(path)).toAbsolutePath()).toInt()) - } - } - - // TODO class GetFreeSpace - - /** - * difference with ComputerCraft: it returns boolean, true on successful. - */ - class Mkdir(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - return LuaValue.valueOf(File(computer.getRealPath(path)).absoluteFile.mkdir()) - } - } - - /** - * moves a directory, overwrites the target - */ - class Mv(val computer: TerrarumComputer) : TwoArgFunction() { - override fun call(from: LuaValue, to: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(from) - Filesystem.ensurePathSanity(to) - - val fromFile = File(computer.getRealPath(from)).absoluteFile - var success = fromFile.copyRecursively( - File(computer.getRealPath(to)).absoluteFile, overwrite = true - ) - if (success) success = fromFile.deleteRecursively() - else return LuaValue.valueOf(false) - return LuaValue.valueOf(success) - } - } - - /** - * copies a directory, overwrites the target - * difference with ComputerCraft: it returns boolean, true on successful. - */ - class Cp(val computer: TerrarumComputer) : TwoArgFunction() { - override fun call(from: LuaValue, to: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(from) - Filesystem.ensurePathSanity(to) - - return LuaValue.valueOf( - File(computer.getRealPath(from)).absoluteFile.copyRecursively( - File(computer.getRealPath(to)).absoluteFile, overwrite = true - ) - ) - } - } - - /** - * difference with ComputerCraft: it returns boolean, true on successful. - */ - class Rm(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - return LuaValue.valueOf( - File(computer.getRealPath(path)).absoluteFile.deleteRecursively() - ) - } - } - - class ConcatPath(val computer: TerrarumComputer) : TwoArgFunction() { - override fun call(base: LuaValue, local: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(base) - Filesystem.ensurePathSanity(local) - - val combinedPath = combinePath(base.checkIBM437().validatePath(), local.checkIBM437().validatePath()) - return LuaValue.valueOf(combinedPath) - } - } - - /** - * @param mode: r, rb, w, wb, a, ab - * - * Difference: TEXT MODE assumes CP437 instead of UTF-8! - * - * When you have opened a file you must always close the file handle, or else data may not be saved. - * - * FILE class in CC: - * (when you look thru them using file = fs.open("./test", "w") - * - * file = { - * close = function() - * -- write mode - * write = function(string) - * flush = function() -- write, keep the handle - * writeLine = function(string) -- text mode - * -- read mode - * readLine = function() -- text mode - * readAll = function() - * -- binary read mode - * read = function() -- read single byte. return: number or nil - * -- binary write mode - * write = function(byte) - * writeBytes = function(string as bytearray) - * } - */ - class OpenFile(val computer: TerrarumComputer) : TwoArgFunction() { - override fun call(path: LuaValue, mode: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - - - val mode = mode.checkIBM437().toLowerCase() - val luaClass = LuaTable() - val file = File(computer.getRealPath(path)).absoluteFile - - if (mode.contains(Regex("""[aw]""")) && !file.canWrite()) - throw LuaError("Cannot open file for " + - "${if (mode.startsWith('w')) "read" else "append"} mode" + - ": is readonly.") - - - - - when (mode) { - "r" -> { - try { - val fr = FileReader(file) - luaClass["close"] = FileClassClose(fr) - luaClass["readLine"] = FileClassReadLine(fr) - luaClass["readAll"] = FileClassReadAll(file.toPath()) - } - catch (e: FileNotFoundException) { - e.printStackTrace() - throw LuaError("$path: no such file.") - } - } - "rb" -> { - try { - val fis = FileInputStream(file) - luaClass["close"] = FileClassClose(fis) - luaClass["read"] = FileClassReadByte(fis) - luaClass["readAll"] = FileClassReadAll(file.toPath()) - } - catch (e: FileNotFoundException) { - e.printStackTrace() - throw LuaError("$path: no such file.") - } - } - "w", "a" -> { - try { - val fw = FileWriter(file, (mode.startsWith('a'))) - luaClass["close"] = FileClassClose(fw) - luaClass["write"] = FileClassPrintText(fw) - luaClass["writeLine"] = FileClassPrintlnText(fw) - luaClass["flush"] = FileClassFlush(fw) - } - catch (e: FileNotFoundException) { - e.printStackTrace() - throw LuaError("$path: is a directory.") - } - } - "wb", "ab" -> { - try { - val fos = FileOutputStream(file, (mode.startsWith('a'))) - luaClass["close"] = FileClassClose(fos) - luaClass["write"] = FileClassWriteByte(fos) - luaClass["writeBytes"] = FileClassWriteBytes(fos) - luaClass["flush"] = FileClassFlush(fos) - } - catch (e: FileNotFoundException) { - e.printStackTrace() - throw LuaError("$path: is a directory.") - } - } - } - - return luaClass - } - } - - class GetParentDir(val computer: TerrarumComputer) : OneArgFunction() { - override fun call(path: LuaValue) : LuaValue { - Filesystem.ensurePathSanity(path) - - var pathSB = StringBuilder(path.checkIBM437()) - - // backward travel, drop chars until '/' has encountered - while (!pathSB.endsWith('/')) - pathSB.deleteCharAt(pathSB.lastIndex - 1) - - // drop trailing '/' - if (pathSB.endsWith('/')) - pathSB.deleteCharAt(pathSB.lastIndex - 1) - - return LuaValue.valueOf(pathSB.toString()) - } - } - - - ////////////////////////////// - // OpenFile implementations // - ////////////////////////////// - - private class FileClassClose(val fo: Any) : ZeroArgFunction() { - override fun call() : LuaValue { - if (fo is FileOutputStream) - fo.close() - else if (fo is FileWriter) - fo.close() - else if (fo is FileReader) - fo.close() - else if (fo is FileInputStream) - fo.close() - else - throw IllegalArgumentException("Unacceptable file output: must be either Input/OutputStream or Reader/Writer.") - - return LuaValue.NONE - } - } - - private class FileClassWriteByte(val fos: FileOutputStream) : OneArgFunction() { - override fun call(byte: LuaValue) : LuaValue { - fos.write(byte.checkint()) - - return LuaValue.NONE - } - } - - private class FileClassWriteBytes(val fos: FileOutputStream) : OneArgFunction() { - override fun call(byteString: LuaValue) : LuaValue { - val byteString = byteString.checkIBM437() - val bytearr = ByteArray(byteString.length, { byteString[it].toByte() }) - fos.write(bytearr) - - return LuaValue.NONE - } - } - - private class FileClassPrintText(val fw: FileWriter) : OneArgFunction() { - override fun call(string: LuaValue) : LuaValue { - val text = string.checkIBM437() - fw.write(text) - return LuaValue.NONE - } - } - - private class FileClassPrintlnText(val fw: FileWriter) : OneArgFunction() { - override fun call(string: LuaValue) : LuaValue { - val text = string.checkIBM437() + "\n" - fw.write(text) - return LuaValue.NONE - } - } - - private class FileClassFlush(val fo: Any) : ZeroArgFunction() { - override fun call() : LuaValue { - if (fo is FileOutputStream) - fo.flush() - else if (fo is FileWriter) - fo.flush() - else - throw IllegalArgumentException("Unacceptable file output: must be either OutputStream or Writer.") - - return LuaValue.NONE - } - } - - private class FileClassReadByte(val fis: FileInputStream) : ZeroArgFunction() { - override fun call() : LuaValue { - val readByte = fis.read() - return if (readByte == -1) LuaValue.NIL else LuaValue.valueOf(readByte) - } - } - - private class FileClassReadAllBytes(val path: Path) : ZeroArgFunction() { - override fun call() : LuaValue { - val byteArr = Files.readAllBytes(path) - val s: String = java.lang.String(byteArr, "IBM437").toString() - return LuaValue.valueOf(s) - } - } - - private class FileClassReadAll(val path: Path) : ZeroArgFunction() { - override fun call() : LuaValue { - return FileClassReadAllBytes(path).call() - } - } - - /** returns NO line separator! */ - private class FileClassReadLine(val fr: FileReader) : ZeroArgFunction() { - val scanner = Scanner(fr.readText()) // no closing; keep the scanner status persistent - - override fun call() : LuaValue { - return if (scanner.hasNextLine()) LuaValue.valueOf(scanner.nextLine()) - else LuaValue.NIL - } - } -} \ No newline at end of file diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt index 91a95a9e2..0d89202d0 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -91,7 +91,7 @@ object WeatherMixer { ) Terrarum.ingame!!.addParticle(rainParticle) } - globalLightNow.set(getGlobalLightOfTime(world.time.elapsedSeconds).darker(0.3f)) + globalLightNow.set(getGlobalLightOfTime(world.time.todaySeconds).darker(0.3f)) } } @@ -105,7 +105,7 @@ object WeatherMixer { fun render(g: Graphics) { // we will not care for nextSkybox for now - val timeNow = Terrarum.ingame!!.world.time.elapsedSeconds + val timeNow = Terrarum.ingame!!.world.time.todaySeconds val skyboxColourMap = currentWeather.skyboxGradColourMap val lightColourMap = currentWeather.globalLightColourMap