fixed timers firing continuously after setting the time to a future date; fixed date stuff after realizing /time set sets world time absolutely (too used to nei by far...); added proper os.date implementation;

This commit is contained in:
Florian Nücke 2013-11-07 17:02:25 +01:00
parent eb3a8e1ff8
commit 2a48cb28d9
8 changed files with 163 additions and 24 deletions

View File

@ -214,7 +214,11 @@ sandbox = {
os = { os = {
clock = os.clock, clock = os.clock,
date = os.date, date = function(format, time)
checkArg(1, format, "string", "nil")
checkArg(2, time, "number", "nil")
return os.date(format, time)
end,
difftime = function(t2, t1) difftime = function(t2, t1)
return t2 - t1 return t2 - t1
end, end,

View File

@ -0,0 +1 @@
print(os.date("%F %T"))

View File

@ -45,7 +45,7 @@ local function tick()
if timer.times <= 0 then if timer.times <= 0 then
timers[id] = nil timers[id] = nil
else else
timer.after = timer.after + timer.interval timer.after = os.uptime() + timer.interval
end end
end end
end end

View File

@ -72,6 +72,7 @@ class Computer(isClient: Boolean) extends Rotatable with ComputerEnvironment wit
super.updateEntity() super.updateEntity()
if (node != null && node.network == null) { if (node != null && node.network == null) {
Network.joinOrCreateNetwork(worldObj, xCoord, yCoord, zCoord) Network.joinOrCreateNetwork(worldObj, xCoord, yCoord, zCoord)
this.synchronized(powerConsumed = 0.0)
} }
else if (!worldObj.isRemote) { else if (!worldObj.isRemote) {
// If we just joined a network we were just loaded from disk. We skip the // If we just joined a network we were just loaded from disk. We skip the

View File

@ -13,7 +13,7 @@ import li.cil.oc.api.network._
import li.cil.oc.common.tileentity import li.cil.oc.common.tileentity
import li.cil.oc.server import li.cil.oc.server
import li.cil.oc.util.ExtendedLuaState.extendLuaState import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.LuaStateFactory import li.cil.oc.util.{GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{OpenComputers, Config} import li.cil.oc.{OpenComputers, Config}
import net.minecraft.nbt._ import net.minecraft.nbt._
import net.minecraft.tileentity.TileEntity import net.minecraft.tileentity.TileEntity
@ -112,7 +112,7 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
switchTo(Computer.State.Starting) switchTo(Computer.State.Starting)
// Remember when we started, for os.clock(). // Remember when we started, for os.clock().
timeStarted = owner.world.getWorldInfo.getWorldTotalTime timeStarted = owner.world.getWorldTime
// Mark state change in owner, to send it to clients. // Mark state change in owner, to send it to clients.
owner.markAsChanged(8) // Initial power required to start. owner.markAsChanged(8) // Initial power required to start.
@ -214,12 +214,12 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
// pause. // pause.
lastUpdate = System.currentTimeMillis lastUpdate = System.currentTimeMillis
// TODO This seems to be the "run time", not the elapsed ingame time. For example, when doing /time set 0 the game // Update world time for time().
// should jump to the next day, but this value does not jump. Is this just Forge or do we have to find some other worldTime = owner.world.getWorldTime
// way around this? CC seems to use getWorldTime, which is really odd, since that should be only within the range if (isRunning) {
// of a single day (0 to 24000), which it *is*... perhaps vanilla Minecraft (not re-compiled) behaves different? // We can have rollbacks from '/time set'. Avoid getting negative uptimes.
// Update world time for computer threads. timeStarted = timeStarted min worldTime
worldTime = owner.world.getWorldInfo.getWorldTotalTime }
// Check if we should switch states. // Check if we should switch states.
state.synchronized(state.top match { state.synchronized(state.top match {
@ -231,7 +231,6 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
// Resuming after being loaded. // Resuming after being loaded.
case Computer.State.Resuming if System.currentTimeMillis() >= sleepUntil => { case Computer.State.Resuming if System.currentTimeMillis() >= sleepUntil => {
verifyComponents() verifyComponents()
owner.markAsChanged(Double.NegativeInfinity)
state.pop() state.pop()
switchTo(state.top) // Trigger execution if necessary. switchTo(state.top) // Trigger execution if necessary.
} }
@ -622,13 +621,57 @@ class Computer(val owner: Computer.Environment) extends Persistable with Runnabl
}) })
lua.setField(-2, "clock") lua.setField(-2, "clock")
// Date formatting function.
lua.pushScalaFunction(lua => {
val format =
if (lua.getTop > 0 && lua.isString(1)) lua.toString(1)
else "%d/%m/%y %H:%M:%S"
val time =
if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) * 1000 / 60 / 60
else worldTime + 6000
val dt = GameTimeFormatter.parse(time)
def fmt(format: String) {
if (format == "*t") {
lua.newTable(0, 8)
lua.pushInteger(dt.year)
lua.setField(-2, "year")
lua.pushInteger(dt.month)
lua.setField(-2, "month")
lua.pushInteger(dt.day)
lua.setField(-2, "day")
lua.pushInteger(dt.hour)
lua.setField(-2, "hour")
lua.pushInteger(dt.minute)
lua.setField(-2, "min")
lua.pushInteger(dt.second)
lua.setField(-2, "sec")
lua.pushInteger(dt.weekDay)
lua.setField(-2, "wday")
lua.pushInteger(dt.yearDay)
lua.setField(-2, "yday")
}
else {
lua.pushString(GameTimeFormatter.format(format, dt))
}
}
// Just ignore the allowed leading '!', Minecraft has no time zones...
if (format.startsWith("!"))
fmt(format.substring(1))
else
fmt(format)
1
})
lua.setField(-2, "date")
// Return ingame time for os.time(). // Return ingame time for os.time().
lua.pushScalaFunction(lua => { lua.pushScalaFunction(lua => {
// Game time is in ticks, so that each day has 24000 ticks, meaning // Game time is in ticks, so that each day has 24000 ticks, meaning
// one hour is game time divided by one thousand. Also, Minecraft // one hour is game time divided by one thousand. Also, Minecraft
// starts days at 6 o'clock, so we add those six hours. Thus: // starts days at 6 o'clock, so we add those six hours. Thus:
// timestamp = (time + 6000) / 1000[h] * 60[m] * 60[s] * 1000[ms] // timestamp = (time + 6000) * 60[kh] * 60[km] / 1000[s]
lua.pushNumber((worldTime + 6000) * 60 * 60) lua.pushNumber((worldTime + 6000) * 60 * 60 / 1000)
1 1
}) })
lua.setField(-2, "time") lua.setField(-2, "time")

View File

@ -33,7 +33,7 @@ class RedstoneCard extends ManagedComponent {
@LuaCallback("setOutput") @LuaCallback("setOutput")
def setOutput(context: Context, args: Arguments): Array[AnyRef] = { def setOutput(context: Context, args: Arguments): Array[AnyRef] = {
val side = checkSide(args, 0) val side = checkSide(args, 0)
val value = args.checkInteger(1) max 0 min 15 val value = args.checkInteger(1) max 0 min 255
node.network.node(context.address).host match { node.network.node(context.address).host match {
case redstone: Redstone => case redstone: Redstone =>
redstone.output(ForgeDirection.getOrientation(side), value.toShort) redstone.output(ForgeDirection.getOrientation(side), value.toShort)

View File

@ -0,0 +1,100 @@
package li.cil.oc.util
import scala.collection.mutable
object GameTimeFormatter {
// Locale? What locale? Seriously though, since this would depend on the
// server's locale I think it makes more sense to keep it English always.
private val weekDays = Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
private val shortWeekDays = Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
private val months = Array("January", "Febuary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December")
private val shortMonths = Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
private val amPm = Array("AM", "PM")
class DateTime(val year: Int, val month: Int, val day: Int,
val weekDay: Int, val yearDay: Int,
val hour: Int, val minute: Int, val second: Int)
// See http://www.cplusplus.com/reference/ctime/strftime/
private val specifiers: Map[Char, (DateTime) => String] = Map(
'a' -> (t => shortWeekDays(t.weekDay)),
'A' -> (t => weekDays(t.weekDay)),
'b' -> (t => shortMonths(t.month)),
'B' -> (t => months(t.month)),
'c' -> (t => format("%a %b %d %H:%M:%S %Y", t)),
'C' -> (t => "%02d".format(t.year / 100)),
'd' -> (t => "%02d".format(t.day + 1)),
'D' -> (t => format("%m/%d/%y", t)),
'e' -> (t => "% 2d".format(t.day + 1)),
'F' -> (t => format("%Y-%m-%d", t)),
//'g' -> (t => ""),
//'G' -> (t => ""),
'h' -> (t => format("%b", t)),
'H' -> (t => "%02d".format(t.hour)),
'I' -> (t => "%02d".format(t.hour % 12 + 1)),
'j' -> (t => "%03d".format(t.yearDay)),
'm' -> (t => "%02d".format(t.month + 1)),
'M' -> (t => "%02d".format(t.minute)),
'n' -> (t => "\n"),
'p' -> (t => amPm(if (t.hour < 12) 0 else 1)),
'r' -> (t => format("%I:%M:%S %p", t)),
'R' -> (t => format("%H:%M", t)),
'S' -> (t => "%02d".format(t.second)),
't' -> (t => "\t"),
'T' -> (t => format("%H:%M:%S", t)),
'u' -> (t => ""),
//'U' -> (t => ""),
//'V' -> (t => ""),
'w' -> (t => "%d".format(t.weekDay)),
//'W' -> (t => ""),
'x' -> (t => format("%D", t)),
'X' -> (t => format("%T", t)),
'y' -> (t => "%02d".format(t.year % 100)),
'Y' -> (t => "%04d".format(t.year)),
//'z' -> (t => ""),
//'Z' -> (t => ""),
'%' -> (t => "%")
)
def parse(time: Double) = {
var day = (time / 24000).toLong
val weekDay = ((4 + day) % 7).toInt
val year = 1970 + (day / 365.2425).toInt
val yearDay = (day % 365.2425).toInt
day = yearDay
val monthLengths =
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
else
Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
var month = 0
while (day > monthLengths(month)) {
day = day - monthLengths(month)
month = month + 1
}
var seconds = ((time % 24000) * 60 * 60 / 1000).toInt
var minutes = seconds / 60
seconds = seconds % 60
val hours = (minutes / 60) % 24
minutes = minutes % 60
new DateTime(year, month, day.toInt, weekDay, yearDay, hours, minutes, seconds)
}
def format(format: String, time: DateTime) = {
val result = new mutable.StringBuilder()
val iterator = format.iterator
while (iterator.hasNext) {
iterator.next() match {
case '%' if iterator.hasNext =>
specifiers.get(iterator.next()) match {
case Some(specifier) => result.append(specifier(time))
case _ =>
}
case c => result.append(c)
}
}
result.toString()
}
}

View File

@ -5,7 +5,6 @@ import com.naef.jnlua.{LuaState, NativeSupport}
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.nio.channels.Channels import java.nio.channels.Channels
import java.util.{Locale, Calendar}
import li.cil.oc.server.component.Computer import li.cil.oc.server.component.Computer
import li.cil.oc.util.ExtendedLuaState._ import li.cil.oc.util.ExtendedLuaState._
import li.cil.oc.{OpenComputers, Config} import li.cil.oc.{OpenComputers, Config}
@ -154,15 +153,6 @@ object LuaStateFactory {
}) })
state.setField(-2, "realTime") state.setField(-2, "realTime")
// Date-time formatting using Java's formatting capabilities.
state.pushScalaFunction(lua => {
val calendar = Calendar.getInstance(Locale.ENGLISH)
calendar.setTimeInMillis(lua.checkInteger(1))
// TODO
1
})
state.setField(-2, "date")
// Pop the os table. // Pop the os table.
state.pop(1) state.pop(1)