From 008566f3581553c096577e37dced7d08e29f1521 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 12 Feb 2017 18:51:56 +0100 Subject: [PATCH] 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. --- evennia/commands/default/system.py | 34 +++--- evennia/contrib/extended_room.py | 4 +- evennia/server/deprecations.py | 46 +++----- evennia/server/evennia_launcher.py | 35 +++--- evennia/server/initial_setup.py | 3 +- evennia/settings_default.py | 19 +-- evennia/utils/gametime.py | 184 ++++++----------------------- 7 files changed, 96 insertions(+), 229 deletions(-) diff --git a/evennia/commands/default/system.py b/evennia/commands/default/system.py index 23e40894c..06a966309 100644 --- a/evennia/commands/default/system.py +++ b/evennia/commands/default/system.py @@ -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): diff --git a/evennia/contrib/extended_room.py b/evennia/contrib/extended_room.py index 26a4c93f4..6bc8ccecb 100644 --- a/evennia/contrib/extended_room.py +++ b/evennia/contrib/extended_room.py @@ -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) diff --git a/evennia/server/deprecations.py b/evennia/server/deprecations.py index d922c01fe..598e409d8 100644 --- a/evennia/server/deprecations.py +++ b/evennia/server/deprecations.py @@ -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 diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 9ccc5f720..96db22fd1 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -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): """ diff --git a/evennia/server/initial_setup.py b/evennia/server/initial_setup.py index afe3d631d..4cdfc8763 100644 --- a/evennia/server/initial_setup.py +++ b/evennia/server/initial_setup.py @@ -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') diff --git a/evennia/settings_default.py b/evennia/settings_default.py index fb872e70c..a7e793380 100644 --- a/evennia/settings_default.py +++ b/evennia/settings_default.py @@ -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 diff --git a/evennia/utils/gametime.py b/evennia/utils/gametime.py index 54f586144..67ec2c42a 100644 --- a/evennia/utils/gametime.py +++ b/evennia/utils/gametime.py @@ -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 -