Reworked how level completions work
This commit is contained in:
parent
94bf0654a8
commit
cb3752dcb7
@ -4,9 +4,9 @@ from django.views.generic import TemplateView
|
|||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from bloonsa_game.models import Level, LevelRating
|
from bloonsa_game.models import Level, LevelRating, LevelScore
|
||||||
from users.models import Player
|
from users.models import Player
|
||||||
from users.util import tag_player
|
from users.util import bloonsa_util, actions
|
||||||
|
|
||||||
|
|
||||||
class CSRFexemptTemplateView(TemplateView):
|
class CSRFexemptTemplateView(TemplateView):
|
||||||
@ -17,18 +17,21 @@ class CSRFexemptTemplateView(TemplateView):
|
|||||||
|
|
||||||
class LoadLevel(CSRFexemptTemplateView):
|
class LoadLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
levelId = request.POST.get("levelId")
|
level_id = request.POST.get("level_id")
|
||||||
if levelId is None or not levelId.isdigit():
|
if level_id is None or not level_id.isdigit():
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
level = Level.objects.filter(levelId=int(levelId)).first()
|
level = Level.objects.filter(levelId=int(level_id)).first()
|
||||||
if level is None:
|
if level is None:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
player = Player.objects.get(user=request.user)
|
player = Player.objects.get(user=request.user)
|
||||||
player.bloonsa_levelsPlayed.add(level)
|
player.bloonsa_levelsPlayed.add(level)
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_load_level_by_id,
|
||||||
|
note=level.levelId)
|
||||||
|
|
||||||
flashVars = level.getFlashVars(seperator="&")
|
flashVars = level.getFlashVars(seperator="&")
|
||||||
return HttpResponse(flashVars)
|
return HttpResponse(flashVars)
|
||||||
@ -40,9 +43,12 @@ class RandomLevel(CSRFexemptTemplateView):
|
|||||||
flashVars = level.getFlashVars(seperator="&")
|
flashVars = level.getFlashVars(seperator="&")
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
player = Player.objects.get(user=request.user)
|
player = Player.objects.get(user=request.user)
|
||||||
player.bloonsa_levelsPlayed.add(level)
|
player.bloonsa_levelsPlayed.add(level)
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_load_random_level,
|
||||||
|
note=level.levelId)
|
||||||
|
|
||||||
return HttpResponse(flashVars)
|
return HttpResponse(flashVars)
|
||||||
|
|
||||||
@ -52,11 +58,31 @@ class RandomLevel(CSRFexemptTemplateView):
|
|||||||
class CompleteLevel(CSRFexemptTemplateView):
|
class CompleteLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
levelId = int(request.POST.get("levelId"))
|
level_id = int(request.POST.get("level_id"))
|
||||||
level = Level.objects.get(levelId=levelId)
|
darts_left = int(request.POST.get("darts_left"))
|
||||||
|
pops = int(request.POST.get("pops"))
|
||||||
|
|
||||||
|
level = Level.objects.get(levelId=level_id)
|
||||||
player = Player.objects.get(user=request.user)
|
player = Player.objects.get(user=request.user)
|
||||||
player.bloonsa_levelsBeaten.add(level)
|
|
||||||
|
prevScore = player.bloonsa_level_scores.first()
|
||||||
|
if prevScore is None \
|
||||||
|
or pops > prevScore.pops \
|
||||||
|
or pops == prevScore.pops and darts_left > prevScore.darts_left:
|
||||||
|
score = LevelScore.objects.create(level=level,
|
||||||
|
clear=True,
|
||||||
|
darts_left=darts_left,
|
||||||
|
pops=pops)
|
||||||
|
if prevScore:
|
||||||
|
player.bloonsa_level_scores.remove(prevScore)
|
||||||
|
|
||||||
|
player.bloonsa_level_scores.add(score)
|
||||||
|
score.save()
|
||||||
|
player.save()
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_submit_score,
|
||||||
|
note=level.levelId)
|
||||||
|
|
||||||
# Sending empty content means error in as3
|
# Sending empty content means error in as3
|
||||||
return HttpResponse(content="GG", status=200)
|
return HttpResponse(content="GG", status=200)
|
||||||
@ -64,16 +90,15 @@ class CompleteLevel(CSRFexemptTemplateView):
|
|||||||
class RateLevel(CSRFexemptTemplateView):
|
class RateLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
rating = int(request.POST.get("rating"))
|
rating = int(request.POST.get("rating"))
|
||||||
levelId = int(request.POST.get("levelId"))
|
level_id = int(request.POST.get("level_id"))
|
||||||
level = Level.objects.get(levelId=levelId)
|
level = Level.objects.get(levelId=level_id)
|
||||||
player = Player.objects.get(user=request.user)
|
player = Player.objects.get(user=request.user)
|
||||||
ratingObject = Player.objects.filter(bloonsa_levelRatings__level=level).first()
|
ratingObject = Player.objects.filter(bloonsa_levelRatings__level=level).first()
|
||||||
if ratingObject:
|
if ratingObject:
|
||||||
ratingObject.rating = rating
|
ratingObject.rating = rating
|
||||||
ratingObject.save()
|
ratingObject.save()
|
||||||
return HttpResponse(content="OK", status=200)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
rating = LevelRating.objects.create(level=level,
|
rating = LevelRating.objects.create(level=level,
|
||||||
@ -81,6 +106,10 @@ class RateLevel(CSRFexemptTemplateView):
|
|||||||
player.bloonsa_levelRatings.add(rating)
|
player.bloonsa_levelRatings.add(rating)
|
||||||
rating.save()
|
rating.save()
|
||||||
player.save()
|
player.save()
|
||||||
|
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_rate_level,
|
||||||
|
note=level.levelId)
|
||||||
return HttpResponse(content="OK", status=200)
|
return HttpResponse(content="OK", status=200)
|
||||||
|
|
||||||
return HttpResponse(status=400)
|
return HttpResponse(status=400)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Level, Author
|
from .models import Level, Author, LevelScore
|
||||||
|
|
||||||
admin.site.register(Level)
|
admin.site.register(Level)
|
||||||
admin.site.register(Author)
|
admin.site.register(Author)
|
||||||
|
admin.site.register(LevelScore)
|
||||||
22
app/bloonsa_game/migrations/0012_levelscore.py
Normal file
22
app/bloonsa_game/migrations/0012_levelscore.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-13 21:27
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bloonsa_game', '0011_levelrating'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='LevelScore',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('clear', models.BooleanField(default=True)),
|
||||||
|
('darts_left', models.PositiveSmallIntegerField(null=True)),
|
||||||
|
('pops', models.PositiveSmallIntegerField(null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
19
app/bloonsa_game/migrations/0013_levelscore_level.py
Normal file
19
app/bloonsa_game/migrations/0013_levelscore_level.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-13 22:14
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bloonsa_game', '0012_levelscore'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='levelscore',
|
||||||
|
name='level',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='bloonsa_game.level'),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
app/bloonsa_game/migrations/0014_alter_levelscore_level.py
Normal file
20
app/bloonsa_game/migrations/0014_alter_levelscore_level.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-13 22:15
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bloonsa_game', '0013_levelscore_level'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='levelscore',
|
||||||
|
name='level',
|
||||||
|
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='bloonsa_game.level'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -42,4 +42,18 @@ class LevelRating(models.Model):
|
|||||||
rating = models.SmallIntegerField()
|
rating = models.SmallIntegerField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.player.first()} - [<{int(self.rating) * '★'}> - {self.level}]"
|
return (f"{self.player.first().user.username}"
|
||||||
|
f" - [<{int(self.rating) * '★'}> - {self.level.title}]")
|
||||||
|
|
||||||
|
# There should only be 1 score per player
|
||||||
|
# Highest popcount wins
|
||||||
|
class LevelScore(models.Model):
|
||||||
|
level = models.ForeignKey(Level, on_delete=models.CASCADE)
|
||||||
|
clear = models.BooleanField(default=True) # This is for if we ever submit scores for failed attempts
|
||||||
|
darts_left = models.PositiveSmallIntegerField(null=True)
|
||||||
|
pops = models.PositiveSmallIntegerField(null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
clearState = "✅" if self.clear else "❌"
|
||||||
|
return (f"{self.player.first().user.username}'s {clearState} @ {self.level.title}"
|
||||||
|
f"[🎈{self.pops} | 🎯{self.darts_left}]")
|
||||||
Binary file not shown.
Binary file not shown.
@ -5,39 +5,41 @@ from django.views.generic import TemplateView
|
|||||||
from django.views.static import serve
|
from django.views.static import serve
|
||||||
|
|
||||||
from bloonsa_game.models import Level
|
from bloonsa_game.models import Level
|
||||||
from users.util import tag_player, init_player
|
from users.util import bloonsa_util, actions
|
||||||
|
|
||||||
|
|
||||||
class IndexView(TemplateView):
|
class IndexView(TemplateView):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
return render(request, "bloonsa_game/index.html", context={})
|
return render(request, "bloonsa_game/index.html", context={})
|
||||||
|
|
||||||
class TermsView(TemplateView):
|
class TermsView(TemplateView):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
return render(request, "bloonsa_game/terms.html", context={})
|
return render(request, "bloonsa_game/terms.html", context={})
|
||||||
|
|
||||||
class GameView(TemplateView):
|
class GameView(TemplateView):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
# This init is for accounts made with 'createsuperuser' or originating from bloonsb
|
# This init is for accounts made with 'createsuperuser' or originating from bloonsb
|
||||||
player = init_player(request=request)
|
player = bloonsa_util.init_player(request=request)
|
||||||
# TODO get player object here with init_player to use in html template03.3.005
|
# TODO get player object here with init_player to use in html template03.3.005
|
||||||
|
|
||||||
if type(kwargs.get("pk")) is int:
|
if type(kwargs.get("pk")) is int:
|
||||||
level = Level.objects.get(id=kwargs["pk"])
|
level = Level.objects.get(id=kwargs["pk"])
|
||||||
if level:
|
if level:
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_load_level_by_url,
|
||||||
|
note=level)
|
||||||
return render(request, "bloonsa_game/level.html", context={
|
return render(request, "bloonsa_game/level.html", context={
|
||||||
"player": player,
|
"player": player,
|
||||||
"flashVars": level.getFlashVars(seperator="&"),
|
"flashVars": level.getFlashVars(seperator="&"),
|
||||||
"levelTitle": level.title,
|
"levelTitle": level.title,
|
||||||
"levelAuthor": level.author,
|
"levelAuthor": level.author,
|
||||||
})
|
})
|
||||||
|
|
||||||
return render(request, "bloonsa_game/game.html", context={"player": player})
|
return render(request, "bloonsa_game/game.html", context={"player": player})
|
||||||
|
|
||||||
class WIPView(TemplateView):
|
class WIPView(TemplateView):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Player, LevelRating
|
from .models import Player, LevelRating, Log
|
||||||
|
|
||||||
|
|
||||||
class PlayerAdmin(admin.ModelAdmin):
|
class PlayerAdmin(admin.ModelAdmin):
|
||||||
@ -9,3 +9,4 @@ class PlayerAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
admin.site.register(LevelRating)
|
admin.site.register(LevelRating)
|
||||||
admin.site.register(Player, PlayerAdmin)
|
admin.site.register(Player, PlayerAdmin)
|
||||||
|
admin.site.register(Log)
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-13 21:27
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bloonsa_game', '0012_levelscore'),
|
||||||
|
('users', '0013_alter_player_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='player',
|
||||||
|
name='bloonsa_levelsBeaten',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='player',
|
||||||
|
name='bloonsa_level_scores',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='player', to='bloonsa_game.levelscore'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='player',
|
||||||
|
name='bloonsa_levelsPlayed',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='player', to='bloonsa_game.level'),
|
||||||
|
),
|
||||||
|
]
|
||||||
33
app/users/migrations/0015_alter_player_user_log.py
Normal file
33
app/users/migrations/0015_alter_player_user_log.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 02:16
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
import users.models
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0014_remove_player_bloonsa_levelsbeaten_and_more'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='player',
|
||||||
|
name='user',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='player', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Log',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('action', models.PositiveSmallIntegerField(verbose_name=users.models.Log.Actions)),
|
||||||
|
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
('note', models.TextField(null=True)),
|
||||||
|
('player', models.ManyToManyField(related_name='log', to='users.player')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
23
app/users/migrations/0016_remove_log_player_log_player.py
Normal file
23
app/users/migrations/0016_remove_log_player_log_player.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 02:27
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0015_alter_player_user_log'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='log',
|
||||||
|
name='player',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='log',
|
||||||
|
name='player',
|
||||||
|
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='log', to='users.player'),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
app/users/migrations/0017_alter_log_player.py
Normal file
19
app/users/migrations/0017_alter_log_player.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 02:28
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0016_remove_log_player_log_player'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='log',
|
||||||
|
name='player',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='log', to='users.player'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
app/users/migrations/0018_alter_log_action.py
Normal file
18
app/users/migrations/0018_alter_log_action.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 02:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0017_alter_log_player'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='log',
|
||||||
|
name='action',
|
||||||
|
field=models.PositiveSmallIntegerField(choices=[(0, 'login'), (1, 'register'), (2, 'logout'), (100, 'bloonsa_load_level_by_id'), (101, 'bloonsa_load_level_by_url'), (102, 'bloonsa_load_random_level'), (103, 'bloonsa_submit_score'), (104, 'bloonsa_rate_level')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
app/users/migrations/0019_alter_log_action.py
Normal file
18
app/users/migrations/0019_alter_log_action.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 02:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0018_alter_log_action'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='log',
|
||||||
|
name='action',
|
||||||
|
field=models.IntegerField(choices=[(0, 'login'), (1, 'register'), (2, 'logout'), (100, 'bloonsa_load_level_by_id'), (101, 'bloonsa_load_level_by_url'), (102, 'bloonsa_load_random_level'), (103, 'bloonsa_submit_score'), (104, 'bloonsa_rate_level')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -2,14 +2,14 @@ from django.db import models
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from bloonsa_game.models import Level, LevelRating
|
from bloonsa_game.models import Level, LevelRating, LevelScore
|
||||||
|
|
||||||
|
|
||||||
class Player(models.Model):
|
class Player(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="player")
|
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name="player")
|
||||||
# Savedata
|
# Savedata
|
||||||
bloonsa_levelsPlayed = models.ManyToManyField(Level, blank=True, related_name="levelsPlayed")
|
bloonsa_levelsPlayed = models.ManyToManyField(Level, blank=True, related_name="player")
|
||||||
bloonsa_levelsBeaten = models.ManyToManyField(Level, blank=True, related_name="levelsBeaten")
|
bloonsa_level_scores = models.ManyToManyField(LevelScore, blank=True, related_name="player")
|
||||||
bloonsa_levelRatings = models.ManyToManyField(LevelRating, blank=True, related_name="player")
|
bloonsa_levelRatings = models.ManyToManyField(LevelRating, blank=True, related_name="player")
|
||||||
# Logging
|
# Logging
|
||||||
creationIP = models.GenericIPAddressField()
|
creationIP = models.GenericIPAddressField()
|
||||||
@ -22,13 +22,15 @@ class Player(models.Model):
|
|||||||
banned = models.BooleanField(default=False) # Account gets logged out upon logging in
|
banned = models.BooleanField(default=False) # Account gets logged out upon logging in
|
||||||
admin = models.BooleanField(default=False) # Ability to suspend/ban other players
|
admin = models.BooleanField(default=False) # Ability to suspend/ban other players
|
||||||
|
|
||||||
|
|
||||||
|
# If bloonsa_levels_played gets replaced then this needs to be updated
|
||||||
@property
|
@property
|
||||||
def levels_played(self):
|
def levels_played(self):
|
||||||
return self.bloonsa_levelsPlayed.count()
|
return self.bloonsa_levelsPlayed.count()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def levels_beaten(self):
|
def levels_beaten(self):
|
||||||
return self.bloonsa_levelsBeaten.count()
|
return self.bloonsa_level_scores.filter(clear=True).count()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_levels(self):
|
def total_levels(self):
|
||||||
@ -41,10 +43,25 @@ class Player(models.Model):
|
|||||||
"banned": "❌" if self.banned else "",
|
"banned": "❌" if self.banned else "",
|
||||||
"admin": "👑" if self.admin else "",
|
"admin": "👑" if self.admin else "",
|
||||||
}
|
}
|
||||||
states = "".join(statesDict.values())
|
states = "".join(statesDict.values()) + " "
|
||||||
if states:
|
return f"{states}{self.user} - {self.latestIP}".strip(" ")
|
||||||
states += " "
|
|
||||||
|
|
||||||
return f"{states}{self.user} - {self.latestIP}"
|
|
||||||
|
|
||||||
|
|
||||||
|
class Log(models.Model):
|
||||||
|
class Actions(models.IntegerChoices):
|
||||||
|
login = 0, "Logged in"
|
||||||
|
register = 1, "Registered"
|
||||||
|
logout = 2, "Logged out"
|
||||||
|
bloonsa_load_level_by_id = 100, "Loaded a level via ingame ID box"
|
||||||
|
bloonsa_load_level_by_url = 101, "Loaded a level via URL"
|
||||||
|
bloonsa_load_random_level = 102, "Loaded a random level"
|
||||||
|
bloonsa_submit_score = 103, "Submitted a score on level_id"
|
||||||
|
bloonsa_rate_level = 104, "Rated a level"
|
||||||
|
|
||||||
|
player = models.ForeignKey(Player, related_name="log", on_delete=models.CASCADE, null=True)
|
||||||
|
action = models.IntegerField(choices=Actions)
|
||||||
|
timestamp = models.DateTimeField(default=timezone.now)
|
||||||
|
note = models.TextField(null=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.player.user.username} - {self.get_action_display()} <{self.note or ''}>"
|
||||||
@ -1,10 +1,16 @@
|
|||||||
from django.contrib.auth import logout
|
from django.contrib.auth import logout
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from users.models import Player
|
from users.models import Player, Log
|
||||||
|
|
||||||
tracking = False
|
actions = Log.Actions
|
||||||
|
|
||||||
|
class BloonsaUtil:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.tracking = True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_ip(request):
|
def get_ip(request):
|
||||||
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
||||||
if x_forwarded_for:
|
if x_forwarded_for:
|
||||||
@ -14,13 +20,13 @@ def get_ip(request):
|
|||||||
return ip
|
return ip
|
||||||
|
|
||||||
# Create a Player object for a User
|
# Create a Player object for a User
|
||||||
def init_player(request):
|
def init_player(self, request):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return
|
return
|
||||||
player = Player.objects.filter(user=request.user).first()
|
player = Player.objects.filter(user=request.user).first()
|
||||||
if player:
|
if player:
|
||||||
return player
|
return player
|
||||||
ip = get_ip(request=request)
|
ip = self.get_ip(request=request)
|
||||||
player = Player(user=request.user,
|
player = Player(user=request.user,
|
||||||
creationIP=ip,
|
creationIP=ip,
|
||||||
latestIP=ip)
|
latestIP=ip)
|
||||||
@ -28,21 +34,33 @@ def init_player(request):
|
|||||||
return player
|
return player
|
||||||
|
|
||||||
# Update activity timestamp and IP
|
# Update activity timestamp and IP
|
||||||
def tag_player(request):
|
def tag_player(self, request):
|
||||||
if not tracking:
|
if not self.tracking:
|
||||||
return
|
return
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return
|
return
|
||||||
player = Player.objects.filter(user=request.user).first()
|
player = Player.objects.filter(user=request.user).first()
|
||||||
if not player:
|
if not player:
|
||||||
init_player(request=request)
|
self.init_player(request=request)
|
||||||
if player.banned:
|
if player.banned:
|
||||||
# TODO message popup?
|
# TODO message popup?
|
||||||
logout(request)
|
logout(request)
|
||||||
return
|
return
|
||||||
|
|
||||||
player.latestActivity = timezone.now()
|
player.latestActivity = timezone.now()
|
||||||
player.latestIP = get_ip(request=request)
|
player.latestIP = self.get_ip(request=request)
|
||||||
player.save()
|
player.save()
|
||||||
|
|
||||||
|
def log(self, action, note, player=None, request=None):
|
||||||
|
if not self.tracking:
|
||||||
|
return
|
||||||
|
if not player:
|
||||||
|
if not request:
|
||||||
|
return
|
||||||
|
player = self.init_player(request=request)
|
||||||
|
item = Log(player=player,
|
||||||
|
action=action,
|
||||||
|
note=note)
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
bloonsa_util = BloonsaUtil()
|
||||||
@ -5,7 +5,7 @@ from django.views.generic import TemplateView
|
|||||||
|
|
||||||
from users.forms import UserRegisterForm, UserLoginForm
|
from users.forms import UserRegisterForm, UserLoginForm
|
||||||
from users.models import Player
|
from users.models import Player
|
||||||
from users.util import init_player, tag_player
|
from users.util import bloonsa_util, actions
|
||||||
|
|
||||||
|
|
||||||
class LoginView(TemplateView):
|
class LoginView(TemplateView):
|
||||||
@ -19,8 +19,10 @@ class LoginView(TemplateView):
|
|||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
return render(request=request, template_name="users/login.html", context={"form": form})
|
return render(request=request, template_name="users/login.html", context={"form": form})
|
||||||
user = form.get_user()
|
user = form.get_user()
|
||||||
init_player(request=request)
|
player = bloonsa_util.init_player(request=request)
|
||||||
login(request=request, user=user)
|
login(request=request, user=user)
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.login)
|
||||||
return redirect("bloonsa_game:game")
|
return redirect("bloonsa_game:game")
|
||||||
|
|
||||||
|
|
||||||
@ -35,8 +37,10 @@ class RegisterView(TemplateView):
|
|||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
return render(request=request, template_name="users/register.html", context={"form": form})
|
return render(request=request, template_name="users/register.html", context={"form": form})
|
||||||
user = form.save()
|
user = form.save()
|
||||||
init_player(request=request)
|
player = bloonsa_util.init_player(request=request)
|
||||||
login(request=request, user=user)
|
login(request=request, user=user)
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.login)
|
||||||
return redirect("bloonsa_game:game")
|
return redirect("bloonsa_game:game")
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +49,9 @@ class LogoutView(TemplateView):
|
|||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
|
bloonsa_util.log(request=request,
|
||||||
|
action=actions.logout)
|
||||||
logout(request)
|
logout(request)
|
||||||
return redirect("bloonsa_game:game")
|
return redirect("bloonsa_game:game")
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user