Move gametime conversion factors to contrib and remove them from the settings file with the motivation that the gametime constats were always highly game specific and a remnant from another time. Reworked the gametime library and add server epoch building from vincent-lg's original PR. The gametime module now deals exclusively in seconds.

This commit is contained in:
Griatch 2017-02-12 18:51:56 +01:00
parent 2a48a4d645
commit 008566f358
7 changed files with 96 additions and 229 deletions

View file

@ -18,7 +18,7 @@ from evennia.server.sessionhandler import SESSIONS
from evennia.scripts.models import ScriptDB
from evennia.objects.models import ObjectDB
from evennia.players.models import PlayerDB
from evennia.utils import logger, utils, gametime, create, prettytable
from evennia.utils import logger, utils, gametime, create
from evennia.utils.eveditor import EvEditor
from evennia.utils.evtable import EvTable
from evennia.utils.utils import crop, class_from_module
@ -540,11 +540,10 @@ class CmdService(COMMAND_DEFAULT_CLASS):
if not switches or switches[0] == "list":
# Just display the list of installed services and their
# status, then exit.
table = prettytable.PrettyTable(["{wService{n (use @services/start|stop|delete)", "{wstatus"])
table.align = 'l'
table = EvTable("{wService{n (use @services/start|stop|delete)", "{wstatus", align="l")
for service in service_collection.services:
table.add_row([service.name, service.running and "{gRunning" or "{rNot Running"])
caller.msg(str(table))
table.add_row(service.name, service.running and "{gRunning" or "{rNot Running")
caller.msg(unicode(table))
return
# Get the service to start / stop
@ -651,19 +650,18 @@ class CmdTime(COMMAND_DEFAULT_CLASS):
def func(self):
"Show server time data in a table."
virtual_epoch = datetime.datetime.fromtimestamp(
gametime.virtual_epoch())
virtual_current = datetime.datetime.fromtimestamp(
gametime.abs_gametime())
table = prettytable.PrettyTable(["{wserver time statistic","{wtime"])
table.align = 'l'
table.add_row(["Current server uptime", utils.time_format(gametime.uptime(), 3)])
table.add_row(["Total server running time", utils.time_format(gametime.runtime(), 2)])
table.add_row(["Game time epoch", virtual_epoch])
table.add_row(["Total in-game time (realtime x %g)" % (gametime.TIMEFACTOR), utils.time_format(gametime.gametime(), 2)])
table.add_row(["Current game time", virtual_current])
table.add_row(["Server time stamp", datetime.datetime.now()])
self.caller.msg(str(table))
table1 = EvTable("|wserver time","|wtime", align="l", width=78)
table1.add_row("Current server uptime", utils.time_format(gametime.uptime(), 3))
table1.add_row("Total server running time", utils.time_format(gametime.runtime(), 2))
table1.add_row("Server epoch (first start)", datetime.datetime.fromtimestamp(gametime.server_epoch()))
table1.add_row("Server time stamp", datetime.datetime.now())
table1.reformat_column(0, width=30)
table2 = EvTable("|wgame time", "|wtime (real x %g)" % gametime.TIMEFACTOR, align="l", width=77, border_top=0)
table2.add_row("Game time epoch", datetime.datetime.fromtimestamp(gametime.game_epoch()))
table2.add_row("Time passed in game:", utils.time_format(gametime.gametime(), 2))
table2.add_row("Current game time", datetime.datetime.fromtimestamp(gametime.gametime(absolute=True)))
table2.reformat_column(0, width=30)
self.caller.msg(unicode(table1) + "\n" + unicode(table2))
class CmdServerLoad(COMMAND_DEFAULT_CLASS):

View file

@ -93,9 +93,9 @@ REGEXMAP = {"morning": (RE_MORNING, RE_AFTERNOON, RE_EVENING, RE_NIGHT),
# set up the seasons and time slots. This assumes gametime started at the
# beginning of the year (so month 1 is equivalent to January), and that
# one CAN divide the game's year into four seasons in the first place ...
MONTHS_PER_YEAR = settings.TIME_MONTH_PER_YEAR
MONTHS_PER_YEAR = 12
SEASONAL_BOUNDARIES = (3 / 12.0, 6 / 12.0, 9 / 12.0)
HOURS_PER_DAY = settings.TIME_HOUR_PER_DAY
HOURS_PER_DAY = 24
DAY_BOUNDARIES = (0, 6 / 24.0, 12 / 24.0, 18 / 24.0)

View file

@ -12,26 +12,11 @@ def check_errors(settings):
Args:
settings (Settings): The Django settings file
Raises:
DeprecationWarning
DeprecationWarning if a critical deprecation is found.
"""
from django.conf import settings
def imp(path, split=True):
mod, fromlist = path, "None"
if split:
mod, fromlist = path.rsplit('.', 1)
__import__(mod, fromlist=[fromlist])
# core modules
imp(settings.COMMAND_PARSER)
imp(settings.SEARCH_AT_RESULT)
imp(settings.CONNECTION_SCREEN_MODULE)
#imp(settings.AT_INITIAL_SETUP_HOOK_MODULE, split=False)
for path in settings.LOCK_FUNC_MODULES:
imp(path, split=False)
# cmdsets
deprstring = ("settings.%s should be renamed to %s. If defaults are used, "
"their path/classname must be updated "
"(see evennia/settings_default.py).")
@ -72,23 +57,22 @@ def check_errors(settings):
"Update your settings file (see evennia/settings_default.py "
"for more info).")
from evennia.commands import cmdsethandler
if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None):
print("Warning: CMDSET_UNLOGGED failed to load!")
if not cmdsethandler.import_cmdset(settings.CMDSET_CHARACTER, None):
print("Warning: CMDSET_CHARACTER failed to load")
if not cmdsethandler.import_cmdset(settings.CMDSET_PLAYER, None):
print("Warning: CMDSET_PLAYER failed to load")
# typeclasses
imp(settings.BASE_PLAYER_TYPECLASS)
imp(settings.BASE_OBJECT_TYPECLASS)
imp(settings.BASE_CHARACTER_TYPECLASS)
imp(settings.BASE_ROOM_TYPECLASS)
imp(settings.BASE_EXIT_TYPECLASS)
imp(settings.BASE_SCRIPT_TYPECLASS)
gametime_deprecation = ("The settings TIME_SEC_PER_MIN, TIME_MIN_PER_HOUR,"
"TIME_HOUR_PER_DAY, TIME_DAY_PER_WEEK, \n"
"TIME_WEEK_PER_MONTH and TIME_MONTH_PER_YEAR "
"are no longer supported. Remove them from your "
"settings file to continue.\nIf you want to use "
"and manipulate these time units, the tools from utils.gametime "
"are now found in contrib/convert_gametime.py instead.")
if any(hasattr(settings, value) for value in ("TIME_SEC_PER_MIN", "TIME_MIN_PER_HOUR",
"TIME_HOUR_PER_DAY", "TIME_DAY_PER_WEEK", "TIME_WEEK_PER_MONTH",
"TIME_MONTH_PER_YEAR")):
raise DeprecationWarning(gametime_deprecation)
def check_warnings(settings):
"""
Check deprecations that should produce warnings but which
does not stop launch.
"""
pass

View file

@ -768,14 +768,11 @@ def error_check_python_modules():
python source files themselves). Best they fail already here
before we get any further.
Raises:
DeprecationWarning: For trying to access various modules
(usually in `settings.py`) which are no longer supported.
"""
from django.conf import settings
def imp(path, split=True):
def _imp(path, split=True):
"helper method"
mod, fromlist = path, "None"
if split:
mod, fromlist = path.rsplit('.', 1)
@ -783,16 +780,20 @@ def error_check_python_modules():
# check the historical deprecations
from evennia.server import deprecations
deprecations.check_errors(settings)
deprecations.check_warnings(settings)
try:
deprecations.check_errors(settings)
deprecations.check_warnings(settings)
except DeprecationWarning as err:
print(err)
sys.exit()
# core modules
imp(settings.COMMAND_PARSER)
imp(settings.SEARCH_AT_RESULT)
imp(settings.CONNECTION_SCREEN_MODULE)
_imp(settings.COMMAND_PARSER)
_imp(settings.SEARCH_AT_RESULT)
_imp(settings.CONNECTION_SCREEN_MODULE)
#imp(settings.AT_INITIAL_SETUP_HOOK_MODULE, split=False)
for path in settings.LOCK_FUNC_MODULES:
imp(path, split=False)
_imp(path, split=False)
from evennia.commands import cmdsethandler
if not cmdsethandler.import_cmdset(settings.CMDSET_UNLOGGEDIN, None):
@ -802,12 +803,12 @@ def error_check_python_modules():
if not cmdsethandler.import_cmdset(settings.CMDSET_PLAYER, None):
print("Warning: CMDSET_PLAYER failed to load")
# typeclasses
imp(settings.BASE_PLAYER_TYPECLASS)
imp(settings.BASE_OBJECT_TYPECLASS)
imp(settings.BASE_CHARACTER_TYPECLASS)
imp(settings.BASE_ROOM_TYPECLASS)
imp(settings.BASE_EXIT_TYPECLASS)
imp(settings.BASE_SCRIPT_TYPECLASS)
_imp(settings.BASE_PLAYER_TYPECLASS)
_imp(settings.BASE_OBJECT_TYPECLASS)
_imp(settings.BASE_CHARACTER_TYPECLASS)
_imp(settings.BASE_ROOM_TYPECLASS)
_imp(settings.BASE_EXIT_TYPECLASS)
_imp(settings.BASE_SCRIPT_TYPECLASS)
def init_game_directory(path, check_db=True):
"""

View file

@ -7,7 +7,7 @@ Everything starts at handle_setup()
"""
from __future__ import print_function
import django
import time
from django.conf import settings
from django.utils.translation import ugettext as _
from evennia.players.models import PlayerDB
@ -160,6 +160,7 @@ def reset_server():
also checks so the warm-reset mechanism works as it should.
"""
ServerConfig.objects.conf("server_epoch", time.time())
from evennia.server.sessionhandler import SESSIONS
logger.log_info(" Initial setup complete. Restarting Server once.")
SESSIONS.server.shutdown(mode='reset')

View file

@ -433,19 +433,12 @@ BASE_BATCHPROCESS_PATHS = ['world', 'evennia.contrib', 'evennia.contrib.tutorial
# The time factor dictates if the game world runs faster (timefactor>1)
# or slower (timefactor<1) than the real world.
TIME_FACTOR = 2.0
# These measures might or might not make sense to your game world.
TIME_SEC_PER_MIN = 60
TIME_MIN_PER_HOUR = 60
TIME_HOUR_PER_DAY = 24
TIME_DAY_PER_WEEK = 7
TIME_WEEK_PER_MONTH = 4
TIME_MONTH_PER_YEAR = 12
# The initial timestamp of your virtual time (in-game)
# You can set this setting to set a fixed, initial timestamp. Your
# game time will be this timestamp plus your current variable game time.
# You can set this setting to a timestamp in 1980, or 2020, or 2500 if
# you want to. Leave it to None to deduce the timestamp from the runtime.
TIME_VIRTUAL_START = None
# The starting point of your game time (the epoch), in seconds.
# In Python a value of 0 means Jan 1 1970. All absolute game times
# will be calculated relative to this. Defaults to the first time
# the server was started. This is mainly useful if you want to
# convert to real-world times.
TIME_GAME_EPOCH = None
######################################################################
# Inlinefunc

View file

@ -14,7 +14,6 @@ from evennia.server.models import ServerConfig
# to real time.
TIMEFACTOR = settings.TIME_FACTOR
TIME_VIRTUAL_START = settings.TIME_VIRTUAL_START
# Only set if gametime_reset was called at some point.
GAME_TIME_OFFSET = ServerConfig.objects.conf("gametime_offset", default=0)
@ -22,55 +21,19 @@ GAME_TIME_OFFSET = ServerConfig.objects.conf("gametime_offset", default=0)
# Common real-life time measure, in seconds.
# You should not change this.
REAL_MIN = 60.0 # seconds per minute in real world
# Game-time units, in real-life seconds. These are supplied as
# a convenient measure for determining the current in-game time,
# e.g. when defining in-game events. The words month, week and year can
# be used to mean whatever units of time are used in the game.
MIN = settings.TIME_SEC_PER_MIN
HOUR = MIN * settings.TIME_MIN_PER_HOUR
DAY = HOUR * settings.TIME_HOUR_PER_DAY
WEEK = DAY * settings.TIME_DAY_PER_WEEK
MONTH = WEEK * settings.TIME_WEEK_PER_MONTH
YEAR = MONTH * settings.TIME_MONTH_PER_YEAR
# these are kept updated by the server maintenance loop
SERVER_START_TIME = 0.0
SERVER_RUNTIME_LAST_UPDATED = 0.0
SERVER_RUNTIME = 0.0
def _format(seconds, *divisors) :
"""
Helper function. Creates a tuple of even dividends given a range
of divisors.
Args:
seconds (int): Number of seconds to format
*divisors (int): a sequence of numbers of integer dividends. The
number of seconds will be integer-divided by the first number in
this sequence, the remainder will be divided with the second and
so on.
Returns:
time (tuple): This tuple has length len(*args)+1, with the
last element being the last remaining seconds not evenly
divided by the supplied dividends.
"""
results = []
seconds = int(seconds)
for divisor in divisors:
results.append(seconds // divisor)
seconds %= divisor
results.append(seconds)
return tuple(results)
# note that these should not be accessed directly since they may
# need further processing. Access from server_epoch() and game_epoch().
_SERVER_EPOCH = None
_GAME_EPOCH = None
# Access functions
def runtime(format=False):
def runtime():
"""
Get the total runtime of the server since first start (minus
downtimes)
@ -83,13 +46,22 @@ def runtime(format=False):
into time units.
"""
rtime = SERVER_RUNTIME + (time.time() - SERVER_RUNTIME_LAST_UPDATED)
if format:
return _format(rtime, 31536000, 2628000, 604800, 86400, 3600, 60)
return rtime
return SERVER_RUNTIME + time.time() - SERVER_RUNTIME_LAST_UPDATED
def uptime(format=False):
def server_epoch():
"""
Get the server epoch. We may need to calculate this on the fly.
"""
global _SERVER_EPOCH
if not _SERVER_EPOCH:
_SERVER_EPOCH = ServerConfig.objects.conf("server_epoch", default=None) \
or time.time() - runtime()
return _SERVER_EPOCH
def uptime():
"""
Get the current uptime of the server since last reload
@ -101,130 +73,48 @@ def uptime(format=False):
into time units.
"""
utime = time.time() - SERVER_START_TIME
if format:
return _format(utime, 31536000, 2628000, 604800, 86400, 3600, 60)
return utime
return time.time() - SERVER_START_TIME
def virtual_epoch():
"""Return the number of VIRTUAL seconds since the server started.
This number is set in the TIME_VIRTUAL_START setting. If not
set, it will be deduced from the current time and server runtime.
Notice, in this case, that it will be slightly fluctuant every
reload or restart.
Returns:
The number of virtual seconds since the game first started.
def game_epoch():
"""
Get the game epoch.
"""
if TIME_VIRTUAL_START is None:
virtual_start = time.time() - runtime()
else:
virtual_start = TIME_VIRTUAL_START
return settings.TIME_GAME_EPOCH or server_epoch()
return virtual_start
def abs_gametime():
"""Return the absolute number of virtual seconds (in game time).
This function returns the number of seconds, using your configured
virtual start (setting TIME_VIRTUAL_START), and adding the
current relative gametime(). If you want to use a standard
calendar, it might save you time and efforts. You could easily
convert the value like this:
>>> from datetime import datetime
>>> current = datetime.fromtimestamp(abs_gametime())
Returns:
The number of virtual seconds using the virtual epoch as a basis.
"""
virtual_start = virtual_epoch()
return virtual_start + gametime()
def gametime(format=False):
def gametime(absolute=False):
"""
Get the total gametime of the server since first start (minus downtimes)
Args:
format (bool, optional): Format into time representation.
absolute (bool, optional): Get the absolute game time, including
the epoch. This could be converted to an absolute in-game
date.
Returns:
time (float or tuple): The gametime or the same time split up
into time units.
Notes:
If one is using a standard calendar, one could convert the unformatted
return to a date using Python's standard `datetime` module like this:
`datetime.datetime.fromtimestamp(gametime(absolute=True))`
"""
gtime = (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR
if format:
return _format(gtime, YEAR, MONTH, WEEK, DAY, HOUR, MIN)
epoch = game_epoch() if absolute else 0
gtime = epoch + (runtime() - GAME_TIME_OFFSET) * TIMEFACTOR
return gtime
def reset_gametime():
"""
Resets the game time to make it start from the current time.
Resets the game time to make it start from the current time. Note that
the epoch set by `settings.TIME_GAME_EPOCH` will still apply.
"""
global GAME_TIME_OFFSET
GAME_TIME_OFFSET = runtime()
ServerConfig.objects.conf("gametime_offset", GAME_TIME_OFFSET)
# Conversion functions
def gametime_to_realtime(secs=0, mins=0, hrs=0, days=0,
weeks=0, months=0, yrs=0, format=False):
"""
This method helps to figure out the real-world time it will take until an
in-game time has passed. E.g. if an event should take place a month later
in-game, you will be able to find the number of real-world seconds this
corresponds to (hint: Interval events deal with real life seconds).
Kwargs:
times (int): The various components of the time.
format (bool): Formatting the output.
Returns:
time (float or tuple): The realtime difference or the same
time split up into time units.
Example:
gametime_to_realtime(days=2) -> number of seconds in real life from
now after which 2 in-game days will have passed.
"""
rtime = (secs + mins * MIN + hrs * HOUR + days * DAY + weeks * WEEK + \
months * MONTH + yrs * YEAR) / TIMEFACTOR
if format:
return _format(rtime, 31536000, 2628000, 604800, 86400, 3600, 60)
return rtime
def realtime_to_gametime(secs=0, mins=0, hrs=0, days=0,
weeks=0, months=0, yrs=0, format=False):
"""
This method calculates how much in-game time a real-world time
interval would correspond to. This is usually a lot less
interesting than the other way around.
Kwargs:
times (int): The various components of the time.
format (bool): Formatting the output.
Returns:
time (float or tuple): The gametime difference or the same
time split up into time units.
Example:
realtime_to_gametime(days=2) -> number of game-world seconds
corresponding to 2 real days.
"""
gtime = TIMEFACTOR * (secs + mins * 60 + hrs * 3600 + days * 86400 +
weeks * 604800 + months * 2628000 + yrs * 31536000)
if format:
return _format(gtime, YEAR, MONTH, WEEK, DAY, HOUR, MIN)
return gtime