enforce exclusivity of only one active Game at a time

a prior commit made this determination for now, for simplicity's sake
(and also what are the odds of running two games at once on the same
codebase), but it was'n really enforced until now

Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
This commit is contained in:
Brian S. Stephan 2024-05-18 09:51:44 -05:00
parent 0ad687669e
commit 4a8babf39e
Signed by: bss
GPG Key ID: 3DE06D3180895FCB
6 changed files with 57 additions and 32 deletions

View File

@ -180,12 +180,11 @@ class IdleRPG(Plugin):
"""Check for characters who have leveled up, and log as such in the channel."""
while self.check_for_level_ups:
time.sleep(self.SLEEP_BETWEEN_LEVEL_CHECKS)
self._level_up_games()
self._level_up_characters()
def _level_up_games(self):
"""Find games under management and level up characters within them, updating their channel."""
logger.debug("checking for level ups in games")
for game in Game.objects.filter(active=True):
def _level_up_characters(self):
"""Level up characters in the active game, updating their channel."""
game = Game.objects.get(active=True)
logger.debug("checking for level ups in %s", game)
for character in Character.objects.levelable(game):
logger.debug("going to try to level up %s", character)

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.5 on 2024-05-18 14:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('idlerpg', '0007_remove_character_one_player_character_per_game_and_more'),
('ircbot', '0020_alter_alias_id_alter_botuser_id_alter_ircchannel_id_and_more'),
]
operations = [
migrations.AddConstraint(
model_name='game',
constraint=models.UniqueConstraint(models.F('active'), condition=models.Q(('active', True)), name='one_enabled_at_a_time'),
),
]

View File

@ -26,7 +26,8 @@ class Game(models.Model):
"""Options for the Game and its objects."""
constraints = [
models.UniqueConstraint("active", "channel", name="one_game_per_channel"),
models.UniqueConstraint('active', 'channel', name='one_game_per_channel'),
models.UniqueConstraint('active', condition=models.Q(active=True), name='one_enabled_at_a_time'),
]
def __str__(self):

View File

@ -37,7 +37,7 @@
"model": "ircbot.ircchannel",
"pk": 2,
"fields": {
"name": "#level_test",
"name": "#duplicate_test",
"server": 1,
"autojoin": false,
"topic_msg": "",
@ -56,15 +56,6 @@
"channel": 1
}
},
{
"model": "idlerpg.game",
"pk": 2,
"fields": {
"name": "level test",
"active": true,
"channel": 2
}
},
{
"model": "idlerpg.character",
"pk": 1,

View File

@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-or-later
"""
import logging
from django.db import transaction
from django.db.utils import IntegrityError
from django.test import TestCase
from ircbot.models import IrcChannel
from idlerpg.models import Game
@ -22,3 +25,16 @@ class GameTest(TestCase):
game = Game.objects.get(pk=1)
logger.debug(str(game))
assert str(game) == "test in #test on default (active)"
def test_cant_have_two_active(self):
"""Test that if we create another game, it's disabled, and can't be active until the first is disabled."""
channel = IrcChannel.objects.get(pk=2)
game = Game.objects.get(pk=1)
new_game = Game.objects.create(name='new one', channel=channel)
assert game.active is True
assert new_game.active is False
with self.assertRaises(IntegrityError):
with transaction.atomic():
new_game.active = True
new_game.save()

View File

@ -262,7 +262,7 @@ class IrcPluginTest(TestCase):
mock_event = mock.MagicMock()
mock_event.source = 'bss!bss@bss_login'
mock_event.recursing = False
game = Game.objects.get(pk=2)
game = Game.objects.get(pk=1)
test_char = Character.objects.register('test_login', game, 'test', 'bss!bss@bss_login', 'tester')
test_char.log_out()
test_char.next_level = datetime.datetime.fromisoformat('2024-05-17 17:00:00-00:00')
@ -283,7 +283,7 @@ class IrcPluginTest(TestCase):
# the ordering of this surprises me... keep an eye on it
self.mock_bot.reply.assert_has_calls([
mock.call(None, "test_login, the level 0 tester, is now online from nickname bss. "
"Next level at 2024-05-17 17:00:00 UTC.", explicit_target='#level_test'),
"Next level at 2024-05-17 17:00:00 UTC.", explicit_target='#test'),
mock.call(mock_event, "test_login, the level 0 tester, has been successfully logged in."),
])
@ -317,7 +317,7 @@ class IrcPluginTest(TestCase):
mock_event = mock.MagicMock()
mock_event.source = 'bss!bss@bss_logout'
mock_event.recursing = False
game = Game.objects.get(pk=2)
game = Game.objects.get(pk=1)
test_char = Character.objects.register('test_logout', game, 'test', 'bss!bss@bss_logout', 'tester')
match = re.match(IdleRPG.LOGOUT_COMMAND_PATTERN, 'LOGOUT')
@ -338,7 +338,7 @@ class IrcPluginTest(TestCase):
mock_event.source = 'bss!bss@bss_remove'
mock_event.recursing = False
game = Game.objects.get(pk=2)
game = Game.objects.get(pk=1)
test_char = Character.objects.register('test_remove', game, 'test', 'bss!bss@bss_remove', 'tester')
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'REMOVEME')
self.plugin.handle_remove(self.mock_connection, mock_event, match)
@ -356,7 +356,7 @@ class IrcPluginTest(TestCase):
mock_event.source = 'bss2!bss@bss_remove'
mock_event.recursing = False
game = Game.objects.get(pk=2)
game = Game.objects.get(pk=1)
test_char = Character.objects.register('test_remove', game, 'test', 'bss!bss@bss_remove', 'tester')
match = re.match(IdleRPG.LOGIN_COMMAND_PATTERN, 'REMOVEME')
self.plugin.handle_remove(self.mock_connection, mock_event, match)
@ -422,9 +422,9 @@ class IrcPluginTest(TestCase):
self.plugin.seen_hostmasks.remove('bss3!bss@bss')
def test_level_up_games(self):
def test_level_up_characters(self):
"""Test the walking of games and characters to level them up."""
game = Game.objects.get(pk=2)
game = Game.objects.get(pk=1)
char_1 = Character.objects.register('test_level_1', game, 'test', 'test_1!test@test', 'tester')
char_2 = Character.objects.register('test_level_2', game, 'test', 'test_2!test@test', 'tester')
char_2.log_out()
@ -434,7 +434,7 @@ class IrcPluginTest(TestCase):
char_1.save()
char_2.save()
# only one should level up, since char_2 isn't logged in
self.plugin._level_up_games()
self.plugin._level_up_characters()
char_1 = Character.objects.get(pk=char_1.pk)
char_2 = Character.objects.get(pk=char_2.pk)