feat: add option to disable pixel placement

This commit is contained in:
2026-10-25 10:42:58 +01:00
parent 4f2cf08b27
commit 6cee1135a8
9 changed files with 70 additions and 19 deletions
+2 -2
View File
@@ -7,8 +7,8 @@ from .models import PixelPlacement, PlaceSettings
@admin.register(PlaceSettings)
class PlaceSettingsAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = ("event", "width", "height")
list_editable = ("width", "height")
list_display = ("event", "width", "height", "placement_enabled")
list_editable = ("width", "height", "placement_enabled")
list_filter = ("event",)
change_actions = ("resync_canvas",)
+13 -1
View File
@@ -45,6 +45,10 @@ class PlaceConsumer(AsyncWebsocketConsumer):
await self.send(text_data=encode("r", "a"))
return
if not place_settings.placement_enabled:
await self.send(text_data=encode("r", "d"))
return
parts = text_data.split("|")
if len(parts) != 4:
await self.send(text_data=encode("r", "i"))
@@ -142,6 +146,7 @@ class PlaceConsumer(AsyncWebsocketConsumer):
next_placement_at,
place_settings.width,
place_settings.height,
int(place_settings.placement_enabled),
)
@database_sync_to_async
@@ -152,4 +157,11 @@ class PlaceConsumer(AsyncWebsocketConsumer):
await self.send(text_data=encode("p", event["x"], event["y"], event["c"]))
async def settings_changed(self, event):
await self.send(text_data=encode("e", event["width"], event["height"]))
await self.send(
text_data=encode(
"e",
event["width"],
event["height"],
int(event["placement_enabled"]),
)
)
@@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-10-25 09:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("place", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="placesettings",
name="placement_enabled",
field=models.BooleanField(default=True),
),
]
+3
View File
@@ -47,6 +47,9 @@ class PlaceSettings(models.Model):
)
width = models.PositiveSmallIntegerField(default=DEFAULT_GRID_WIDTH)
height = models.PositiveSmallIntegerField(default=DEFAULT_GRID_HEIGHT)
# Freezes the board without taking the game down: clients can still
# connect and watch, but every placement is rejected.
placement_enabled = models.BooleanField(default=True)
def __str__(self):
return f"PlaceSettings({self.event}, size={self.width}x{self.height})"
+4 -2
View File
@@ -1,11 +1,12 @@
"""Reacts to an admin editing a PlaceSettings row (grid size) by:
"""Reacts to an admin editing a PlaceSettings row (grid size, placement
toggle) by:
1. Rebuilding that event's Redis canvas from history at the (possibly new)
dimensions - a resize invalidates the old Redis bytes (wrong length,
wrong meaning), so this keeps Redis self-healing instead of relying on
the admin remembering to click "Resync canvas from DB" after every
resize.
2. Pushing the new size to that event's already-connected clients. Unlike
2. Pushing the new settings to that event's already-connected clients. Unlike
the clock app, place has no periodic heartbeat broadcast to piggyback
this onto, so it's broadcast explicitly here instead.
"""
@@ -33,5 +34,6 @@ def on_settings_saved(sender, instance, **kwargs):
"type": "settings_changed",
"width": instance.width,
"height": instance.height,
"placement_enabled": instance.placement_enabled,
},
)
+11 -6
View File
@@ -6,8 +6,10 @@ either). Each message is a short string: a one-letter type tag followed by
'|'-separated fields, in a fixed order the client already knows from this
table:
s|<next>|<width>|<height> initial personal state, once per connection
(next = epoch ms cooldown ends, or empty if free now)
s|<next>|<width>|<height>|<on>
initial personal state, once per connection
(next = epoch ms cooldown ends, or empty if free now;
on = 1 if placing pixels is allowed, 0 if frozen)
p|<x>|<y>|<c> a pixel was placed - broadcast to everyone,
including the placer (no separate ack needed)
k|<next> your own placement was accepted; carries your
@@ -15,10 +17,13 @@ table:
r|c|<next> rejected: still on cooldown
r|a rejected: not logged in
r|i rejected: invalid x/y/color
e|<width>|<height> an admin resized the grid for this event -
broadcast to everyone. A client that sees this
should re-fetch /api/.../place/canvas rather than
try to remap its local pixels.
r|d rejected: placement is disabled by an admin
e|<width>|<height>|<on>
an admin edited this event's settings (grid size
and/or placement toggle) - broadcast to everyone.
If the size changed, a client should re-fetch
/api/.../place/canvas rather than try to remap its
local pixels.
Client -> server has exactly one message shape, the same as the broadcast:
p|<x>|<y>|<c>