async def set_client_location_options(sid: str, data: LocationOptions): pr: PlayerRoom = game_state.get(sid) LocationUserOption.update( pan_x=data["pan_x"], pan_y=data["pan_y"], zoom_factor=data["zoom_factor"], ).where( (LocationUserOption.location == pr.active_location) & (LocationUserOption.user == pr.player) ).execute()
async def set_client(sid: str, data: ClientOptions): pr: PlayerRoom = game_state.get(sid) with db.atomic(): for option, value in data.items(): if option != "location_options": setattr(pr.player, option, value) pr.player.save() if "location_options" in data: LocationUserOption.update( pan_x=data["location_options"]["pan_x"], pan_y=data["location_options"]["pan_y"], zoom_factor=data["location_options"]["zoom_factor"], ).where((LocationUserOption.location == pr.active_location) & (LocationUserOption.user == pr.player)).execute()
async def load_location(sid, location): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] sid_data["location"] = location data = {} data["locations"] = [l.name for l in room.locations] if user == room.creator: data["layers"] = [ l.as_dict(user, True) for l in location.layers.order_by(Layer.index) ] else: data["layers"] = [ l.as_dict(user, False) for l in location.layers.order_by(Layer.index).where(Layer.player_visible) ] client_options = user.as_dict() client_options.update( **LocationUserOption.get(user=user, location=location).as_dict() ) await sio.emit("Board.Set", data, room=sid, namespace="/planarally") await sio.emit( "Location.Set", location.as_dict(), room=sid, namespace="/planarally" ) await sio.emit( "Client.Options.Set", client_options, room=sid, namespace="/planarally" ) await sio.emit( "Notes.Set", [ note.as_dict() for note in Note.select().where((Note.user == user) & (Note.room == room)) ], room=sid, namespace="/planarally", ) sorted_initiatives = [ init.as_dict() for init in Initiative.select() .join(Shape, JOIN.LEFT_OUTER, on=(Initiative.uuid == Shape.uuid)) .join(Layer) .where((Layer.location == location)) .order_by(Initiative.index) ] location_data = InitiativeLocationData.get_or_none(location=location) if location_data: await send_client_initiatives(room, location, user) await sio.emit( "Initiative.Round.Update", location_data.round, room=sid, namespace="/planarally", ) await sio.emit( "Initiative.Turn.Set", location_data.turn, room=sid, namespace="/planarally" )
async def focus_location(sid): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] if room.creator != user: logger.warning(f"{user.name} attempted to focus everyone") return location_dm_option = LocationUserOption.get(user=user, location=location) for room_player in room.players: location_option = LocationUserOption.get(user=room_player.player, location=location) location_option.pan_x = location_dm_option.pan_x location_option.pan_y = location_dm_option.pan_y location_option.zoom_factor = location_dm_option.zoom_factor location_option.save() for psid in state.get_sids(user=room_player.player, room=room): await load_location(psid, location)
async def set_client(sid: int, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) with db.atomic(): for option in [ ("gridColour", "grid_colour"), ("fowColour", "fow_colour"), ("rulerColour", "ruler_colour"), ("invertAlt", "invert_alt"), ]: if option[0] in data: setattr(pr.player, option[1], data[option[0]]) pr.player.save() if "locationOptions" in data: LocationUserOption.update( pan_x=data["locationOptions"]["panX"], pan_y=data["locationOptions"]["panY"], zoom_factor=data["locationOptions"]["zoomFactor"], ).where((LocationUserOption.location == pr.active_location) & (LocationUserOption.user == pr.player)).execute()
async def load_location(sid: int, location: Location): pr: PlayerRoom = game_state.get(sid) if pr.active_location != location: pr.active_location = location pr.save() data = {} data["locations"] = [ {"id": l.id, "name": l.name} for l in pr.room.locations.order_by(Location.index) ] data["floors"] = [ f.as_dict(pr.player, pr.player == pr.room.creator) for f in location.floors.order_by(Floor.index) ] client_options = pr.player.as_dict() client_options.update( **LocationUserOption.get(user=pr.player, location=location).as_dict() ) await sio.emit("Board.Set", data, room=sid, namespace=GAME_NS) await sio.emit("Location.Set", location.as_dict(), room=sid, namespace=GAME_NS) await sio.emit("Client.Options.Set", client_options, room=sid, namespace=GAME_NS) await sio.emit( "Notes.Set", [ note.as_dict() for note in Note.select().where( (Note.user == pr.player) & (Note.room == pr.room) ) ], room=sid, namespace=GAME_NS, ) await sio.emit( "Markers.Set", [ marker.as_string() for marker in Marker.select(Marker.shape_id).where( (Marker.user == pr.player) & (Marker.location == location) ) ], room=sid, namespace=GAME_NS, ) location_data = InitiativeLocationData.get_or_none(location=location) if location_data: await send_client_initiatives(pr, pr.player) await sio.emit( "Initiative.Round.Update", location_data.round, room=sid, namespace=GAME_NS, ) await sio.emit( "Initiative.Turn.Set", location_data.turn, room=sid, namespace=GAME_NS )
async def set_client(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] location = sid_data["location"] with db.atomic(): for option in [ ("gridColour", "grid_colour"), ("fowColour", "fow_colour"), ("rulerColour", "ruler_colour"), ]: if option[0] in data: setattr(user, option[1], data[option[0]]) user.save() if "locationOptions" in data: LocationUserOption.update( pan_x=data["locationOptions"]["panX"], pan_y=data["locationOptions"]["panY"], zoom_factor=data["locationOptions"]["zoomFactor"], ).where((LocationUserOption.location == location) & (LocationUserOption.user == user)).execute()
async def set_layer(sid: int, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) try: floor = pr.active_location.floors.select().where(Floor.name == data["floor"])[0] layer = floor.layers.select().where(Layer.name == data["layer"])[0] except IndexError: pass else: luo = LocationUserOption.get(user=pr.player, location=pr.active_location) luo.active_layer = layer luo.save()
async def set_layer(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] location = sid_data["location"] try: layer = location.layers.select().where(Layer.name == data)[0] except IndexError: pass else: luo = LocationUserOption.get(user=user, location=location) luo.active_layer = layer luo.save()
async def load_location(sid, location): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] sid_data["location"] = location data = {} data["locations"] = [l.name for l in room.locations] data["floors"] = [ f.as_dict(user, user == room.creator) for f in location.floors.order_by(Floor.index) ] client_options = user.as_dict() client_options.update( **LocationUserOption.get(user=user, location=location).as_dict()) await sio.emit("Board.Set", data, room=sid, namespace="/planarally") await sio.emit("Location.Set", location.as_dict(), room=sid, namespace="/planarally") await sio.emit("Client.Options.Set", client_options, room=sid, namespace="/planarally") await sio.emit( "Notes.Set", [ note.as_dict() for note in Note.select().where((Note.user == user) & (Note.room == room)) ], room=sid, namespace="/planarally", ) location_data = InitiativeLocationData.get_or_none(location=location) if location_data: await send_client_initiatives(room, location, user) await sio.emit( "Initiative.Round.Update", location_data.round, room=sid, namespace="/planarally", ) await sio.emit("Initiative.Turn.Set", location_data.turn, room=sid, namespace="/planarally")
async def update_client_location(player: int, room: int, sid: str, data: LocationOptions): pr = PlayerRoom.get(player=player, room=room) LocationUserOption.update( pan_x=data["pan_x"], pan_y=data["pan_y"], zoom_display=data["zoom_display"], ).where((LocationUserOption.location == pr.active_location) & (LocationUserOption.user == pr.player)).execute() if pr.role != Role.DM: for p_sid, p_player in game_state.get_t(skip_sid=sid): if p_player.role == Role.DM or p_player.player.id == player: await sio.emit( "Client.Move", { "player": pr.player.id, **data }, room=p_sid, namespace=GAME_NS, )
def upgrade(version): if version == 3: from models import GridLayer db.execute_sql( "CREATE TEMPORARY TABLE _grid_layer AS SELECT * FROM grid_layer") db.drop_tables([GridLayer]) db.create_tables([GridLayer]) db.execute_sql("INSERT INTO grid_layer SELECT * FROM _grid_layer") Constants.update(save_version=Constants.save_version + 1).execute() elif version == 4: from models import Location db.foreign_keys = False db.execute_sql( "CREATE TEMPORARY TABLE _location AS SELECT * FROM location") db.execute_sql("DROP TABLE location") db.create_tables([Location]) db.execute_sql("INSERT INTO location SELECT * FROM _location") db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 5: from models import Layer migrator = SqliteMigrator(db) field = ForeignKeyField(Layer, Layer.id, backref="active_users", null=True) with db.atomic(): migrate( migrator.add_column("location_user_option", "active_layer_id", field)) from models import LocationUserOption LocationUserOption._meta.add_field("active_layer", field) for luo in LocationUserOption.select(): luo.active_layer = luo.location.layers.select().where( Layer.name == "tokens")[0] luo.save() migrate( migrator.add_not_null("location_user_option", "active_layer_id")) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 6: migrator = SqliteMigrator(db) migrate( migrator.drop_not_null("location_user_option", "active_layer_id")) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 7: # Remove shape index unique constraint from models import Shape db.foreign_keys = False db.execute_sql("CREATE TEMPORARY TABLE _shape AS SELECT * FROM shape") db.execute_sql("DROP TABLE shape") db.create_tables([Shape]) db.execute_sql("INSERT INTO shape SELECT * FROM _shape") db.foreign_keys = True # Check all indices and reset to 0 index logger.info("Validating all shape indices") from models import Layer with db.atomic(): for layer in Layer.select(): shapes = layer.shapes.order_by(fn.ABS(Shape.index)) for i, shape in enumerate(shapes): shape.index = i shape.save() Constants.update(save_version=Constants.save_version + 1).execute() elif version == 8: from models import Polygon db.create_tables([Polygon]) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 9: from models import Location db.foreign_keys = False migrator = SqliteMigrator(db) with db.atomic(): migrate( migrator.add_column("location", "vision_mode", Location.vision_mode), migrator.add_column("location", "vision_min_range", Location.vision_min_range), migrator.add_column("location", "vision_max_range", Location.vision_max_range), ) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() else: raise Exception( f"No upgrade code for save format {version} was found.")
async def load_location(sid: str, location: Location, *, complete=False): pr: PlayerRoom = game_state.get(sid) if pr.active_location != location: pr.active_location = location pr.save() # 1. Load client options client_options = pr.player.as_dict() client_options["location_user_options"] = LocationUserOption.get( user=pr.player, location=location).as_dict() client_options["default_user_options"] = pr.player.default_options.as_dict( ) if pr.user_options: client_options["room_user_options"] = pr.user_options.as_dict() await sio.emit("Client.Options.Set", client_options, room=sid, namespace=GAME_NS) # 2. Load room info if complete: await sio.emit( "Room.Info.Set", { "name": pr.room.name, "creator": pr.room.creator.name, "invitationCode": str(pr.room.invitation_code), "isLocked": pr.room.is_locked, "default_options": pr.room.default_options.as_dict(), "players": [{ "id": rp.player.id, "name": rp.player.name, "location": rp.active_location.id, "role": rp.role, } for rp in pr.room.players], "publicName": config.get("General", "public_name", fallback=""), }, room=sid, namespace=GAME_NS, ) # 3. Load location await sio.emit("Location.Set", location.as_dict(), room=sid, namespace=GAME_NS) # 4. Load all location settings (DM) if complete and pr.role == Role.DM: await sio.emit( "Locations.Settings.Set", { l.id: {} if l.options is None else l.options.as_dict() for l in pr.room.locations }, room=sid, namespace=GAME_NS, ) # 5. Load Board locations = [{ "id": l.id, "name": l.name, "archived": l.archived } for l in pr.room.locations.order_by(Location.index)] await sio.emit("Board.Locations.Set", locations, room=sid, namespace=GAME_NS) floors = [floor for floor in location.floors.order_by(Floor.index)] if "active_floor" in client_options["location_user_options"]: index = next(i for i, f in enumerate(floors) if f.name == client_options["location_user_options"]["active_floor"]) lower_floors = floors[index - 1::-1] if index > 0 else [] higher_floors = floors[index + 1:] if index < len(floors) else [] floors = [floors[index], *lower_floors, *higher_floors] for floor in floors: await sio.emit( "Board.Floor.Set", floor.as_dict(pr.player, pr.role == Role.DM), room=sid, namespace=GAME_NS, ) # 6. Load Initiative location_data = Initiative.get_or_none(location=location) if location_data: await sio.emit("Initiative.Set", location_data.as_dict(), room=sid, namespace=GAME_NS) # 7. Load labels if complete: labels = Label.select().where((Label.user == pr.player) | (Label.visible == True)) label_filters = LabelSelection.select().where( (LabelSelection.user == pr.player) & (LabelSelection.room == pr.room)) await sio.emit( "Labels.Set", [l.as_dict() for l in labels], room=sid, namespace=GAME_NS, ) await sio.emit( "Labels.Filters.Set", [l.label.uuid for l in label_filters], room=sid, namespace=GAME_NS, ) # 8. Load Notes await sio.emit( "Notes.Set", [ note.as_dict() for note in Note.select().where((Note.user == pr.player) & (Note.room == pr.room)) ], room=sid, namespace=GAME_NS, ) # 9. Load Markers await sio.emit( "Markers.Set", [ marker.as_string() for marker in Marker.select(Marker.shape_id).where( (Marker.user == pr.player) & (Marker.location == location)) ], room=sid, namespace=GAME_NS, ) # 10. Load Assets if complete: await sio.emit( "Asset.List.Set", Asset.get_user_structure(pr.player), room=sid, namespace=GAME_NS, )
def upgrade(version): if version == 3: from models import GridLayer db.execute_sql("CREATE TEMPORARY TABLE _grid_layer AS SELECT * FROM grid_layer") db.drop_tables([GridLayer]) db.create_tables([GridLayer]) db.execute_sql("INSERT INTO grid_layer SELECT * FROM _grid_layer") Constants.update(save_version=Constants.save_version + 1).execute() elif version == 4: from models import Location db.foreign_keys = False db.execute_sql("CREATE TEMPORARY TABLE _location AS SELECT * FROM location") db.execute_sql("DROP TABLE location") db.create_tables([Location]) db.execute_sql("INSERT INTO location SELECT * FROM _location") db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 5: from models import Layer migrator = SqliteMigrator(db) field = ForeignKeyField(Layer, Layer.id, backref="active_users", null=True) with db.atomic(): migrate( migrator.add_column("location_user_option", "active_layer_id", field) ) from models import LocationUserOption LocationUserOption._meta.add_field("active_layer", field) for luo in LocationUserOption.select(): luo.active_layer = luo.location.layers.select().where( Layer.name == "tokens" )[0] luo.save() migrate(migrator.add_not_null("location_user_option", "active_layer_id")) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 6: migrator = SqliteMigrator(db) migrate(migrator.drop_not_null("location_user_option", "active_layer_id")) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 7: # Remove shape index unique constraint from models import Shape db.foreign_keys = False db.execute_sql("CREATE TEMPORARY TABLE _shape AS SELECT * FROM shape") db.execute_sql("DROP TABLE shape") db.create_tables([Shape]) db.execute_sql("INSERT INTO shape SELECT * FROM _shape") db.foreign_keys = True # Check all indices and reset to 0 index logger.info("Validating all shape indices") from models import Layer with db.atomic(): for layer in Layer.select(): shapes = layer.shapes.order_by(fn.ABS(Shape.index)) for i, shape in enumerate(shapes): shape.index = i shape.save() Constants.update(save_version=Constants.save_version + 1).execute() elif version == 8: from models import Polygon db.create_tables([Polygon]) Constants.update(save_version=Constants.save_version + 1).execute() elif version == 9: from models import Location db.foreign_keys = False migrator = SqliteMigrator(db) with db.atomic(): migrate( migrator.add_column("location", "vision_mode", Location.vision_mode), migrator.add_column( "location", "vision_min_range", Location.vision_min_range ), migrator.add_column( "location", "vision_max_range", Location.vision_max_range ), ) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 10: from models import Shape db.foreign_keys = False migrator = SqliteMigrator(db) with db.atomic(): migrate(migrator.add_column("shape", "name_visible", Shape.name_visible)) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 11: from models import Label, LocationUserOption, ShapeLabel db.foreign_keys = False migrator = SqliteMigrator(db) with db.atomic(): db.create_tables([Label, ShapeLabel]) migrate( migrator.add_column( "location_user_option", "active_filters", LocationUserOption.active_filters, ) ) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 12: from models import Label, LabelSelection db.foreign_keys = False migrator = SqliteMigrator(db) with db.atomic(): try: migrate(migrator.add_column("label", "category", Label.category)) except OperationalError as e: if e.args[0] != "duplicate column name: category": raise e db.create_tables([LabelSelection]) with db.atomic(): for label in Label: if ":" not in label.name: continue cat, *name = label.name.split(":") label.category = cat label.name = ":".join(name) label.save() db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 13: from models import LocationUserOption, MultiLine, Polygon db.foreign_keys = False migrator = SqliteMigrator(db) migrate(migrator.drop_column("location_user_option", "active_filters")) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 14: db.foreign_keys = False migrator = SqliteMigrator(db) from models import GridLayer, Layer db.execute_sql('CREATE TABLE IF NOT EXISTS "base_rect" ("shape_id" TEXT NOT NULL PRIMARY KEY, "width" REAL NOT NULL, "height" REAL NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)') db.execute_sql('CREATE TABLE IF NOT EXISTS "shape_type" ("shape_id" TEXT NOT NULL PRIMARY KEY, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)') shape_types = [ "asset_rect", "circle", "circular_token", "line", "multi_line", "polygon", "rect", "text", ] with db.atomic(): for table in shape_types: db.execute_sql( f"CREATE TEMPORARY TABLE _{table} AS SELECT * FROM {table}" ) db.execute_sql(f"DROP TABLE {table}") for query in [ 'CREATE TABLE IF NOT EXISTS "asset_rect" ("shape_id" TEXT NOT NULL PRIMARY KEY, "width" REAL NOT NULL, "height" REAL NOT NULL, "src" TEXT NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "circle" ("shape_id" TEXT NOT NULL PRIMARY KEY, "radius" REAL NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "circular_token" ("shape_id" TEXT NOT NULL PRIMARY KEY, "radius" REAL NOT NULL, "text" TEXT NOT NULL, "font" TEXT NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "line" ("shape_id" TEXT NOT NULL PRIMARY KEY, "x2" REAL NOT NULL, "y2" REAL NOT NULL, "line_width" INTEGER NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "multi_line" ("shape_id" TEXT NOT NULL PRIMARY KEY, "line_width" INTEGER NOT NULL, "points" TEXT NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "polygon" ("shape_id" TEXT NOT NULL PRIMARY KEY, "vertices" TEXT NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "rect" ("shape_id" TEXT NOT NULL PRIMARY KEY, "width" REAL NOT NULL, "height" REAL NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', 'CREATE TABLE IF NOT EXISTS "text" ("shape_id" TEXT NOT NULL PRIMARY KEY, "text" TEXT NOT NULL, "font" TEXT NOT NULL, "angle" REAL NOT NULL, FOREIGN KEY ("shape_id") REFERENCES "shape" ("uuid") ON DELETE CASCADE)', ]: db.execute_sql(query) for table in shape_types: db.execute_sql( f"INSERT INTO {table} SELECT _{table}.* FROM _{table} INNER JOIN shape ON shape.uuid = _{table}.uuid" ) field = ForeignKeyField(Layer, Layer.id, null=True) with db.atomic(): migrate(migrator.add_column("grid_layer", "layer_id", field)) for gl in GridLayer.select(): l = Layer.get_or_none(id=gl.id) if l: gl.layer = l gl.save() else: gl.delete_instance() migrate(migrator.add_not_null("grid_layer", "layer_id")) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() elif version == 15: from peewee import BooleanField migrator = SqliteMigrator(db) db.foreign_keys = False with db.atomic(): migrate( migrator.add_column("room", "is_locked", BooleanField(default=False)) ) db.foreign_keys = True Constants.update(save_version=Constants.save_version + 1).execute() else: raise Exception(f"No upgrade code for save format {version} was found.")