Django 回合制浏览器游戏 - 数据库设计以处理独立时间步伐

0 投票
1 回答
1202 浏览
提问于 2025-04-17 23:26

我正在用Python/Django开发一个回合制策略网页游戏。这个游戏的想法是这样的:玩家在第n回合时与网站互动。他们查看自己的游戏状态(游戏“世界”),并下达将在下一个时间步执行的命令。某个时刻,网站会暂停,然后回合引擎会根据第n步的状态和所有玩家的命令,计算出第n+1步的新游戏状态。在这个时间步评估期间,我需要读取第n步的对象状态,并在第n+1步创建/写入对象。怎样做才能最有效呢?(这是关于数据库设计的问题)

我考虑了以下几种方案: 第一种方法是:让每个世界对象都带有一个时间步参数。 这样做可能会很麻烦,因为每次都要根据时间步来筛选。而且,数据库会随着每个时间步的增加而增长,因为它会包含从第0步开始的整个历史记录。

第二种方法是:为每个时间步使用不同的物理数据库。 也就是说,在时间步演变过程中,为第n+1步创建一个新的数据库。在评估时,同时处理数据库(n, n+1)。一旦时间步评估完成,就删除(或者更好:归档)第n步的数据库,并用第n+1步的数据库替换它。(这样做的好处是可以保留每个时间步的数据库快照作为备份) 我觉得后者似乎是更好的方法。但我需要一些建议,关于如何同时处理两个数据库。

你能给我其他建议,帮助我做出选择吗? 你觉得还有其他可能的方法吗? 有没有第三方库或Django插件可以处理类似的问题? 如果我使用第二种方法,如何告诉Django同时使用两个数据库,每个数据库中的对象类型相同?

1 个回答

1

我觉得你大概已经明白了。这里有两个数据库,分别叫做 defaultfuture

DATABASES = {
    'default': {
        'NAME': 'default',
        'ENGINE': 'django.db.backends.mysql',
        'USER': '',
        'PASSWORD': '',
    },
    'future': {
        'NAME': 'future',
        'ENGINE': 'django.db.backends.mysql',
        'USER': '',
        'PASSWORD': '',
    },
}

你可以像往常一样,使用模型来写你的视图或其他东西。这些内容会被写入到 default 数据库,就像你之前习惯的那样。


接下来,创建一个管理命令来更新游戏状态……(你也可以把这段代码放到 Celery 任务里或者其他地方,不过在这个回答中,我打算通过命令行和定时任务来调用它。)

# project/app/management/commands/run_turn.py

from django.conf import settings
from django.core.management.base import BaseCommand
import subprocess
from optparse import make_option

def copy_default_to_future():
    # Copy database before acting on game state
    # use the subprocess library for bash execution of mysql/postgres commands

    # ...

def copy_future_to_default():
    # Copy database after acting on game state
    # use the subprocess library for bash execution of mysql/postgres commands

    # ...

def upload_backup_to_cloud():
    # i recommend using django-cumulus and a custom backups storage container

    # ...

class Command(BaseCommand):
    args = '<attachment_path attachment_path ...>'
    help = 'Processes game state at end of turn'

    option_list = BaseCommand.option_list + (
        make_option('-u', '--upload-backup',
            action='store_true',
            dest='upload',
            default=False,
            help='Upload database export to cloud object storage'),
        )

    def handle(self, *args, **options):
         if options.get('upload', None):
             upload_backup_to_cloud()

         copy_default_to_future()

         # ... change your gamestate
         for player in Player.objects.using('future').all():
             player.increment()
         # ...

         copy_future_to_default()

         print "Game state updated."

patrick@lucca:~$ crontab -e

@hourly /home/patrick/.virtualenvs/browsergame/bin/python /path/to/project/manage.py run_turn --upload-backup

参考资料:

撰写回答