async def remove_label(sid: str, data: ShapeSetStringValue): pr: PlayerRoom = game_state.get(sid) shape = get_shape_or_none(pr, data["shape"], "Label.Remove") if shape is None: return label = ShapeLabel.get_by_id(data["value"]) label.delete_instance(True) await sio.emit( "Shape.Options.Label.Remove", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def set_movement_block(sid: str, data: ShapeSetBooleanValue): pr: PlayerRoom = game_state.get(sid) shape = get_shape_or_none(pr, data["shape"], "MovementBlock.Set") if shape is None: return shape.movement_obstruction = data["value"] shape.save() await sio.emit( "Shape.Options.MovementBlock.Set", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def set_annotation(sid: str, data: ShapeSetStringValue): pr: PlayerRoom = game_state.get(sid) shape = get_shape_or_none(pr, data["shape"], "Annotation.Set") if shape is None: return shape.annotation = data["value"] shape.save() for sid in get_owner_sids(pr, shape, skip_sid=sid): await sio.emit( "Shape.Options.Annotation.Set", data, room=sid, namespace=GAME_NS, )
async def remove_floor(sid, data): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to remove a floor") return floor: Floor = Floor.get(location=pr.active_location, name=data) floor.delete_instance(recursive=True) await sio.emit( "Floor.Remove", data, room=pr.active_location.get_path(), skip_sid=sid, namespace="/planarally", )
async def set_fill_colour(sid: str, data: ShapeSetStringValue): pr: PlayerRoom = game_state.get(sid) shape = get_shape_or_none(pr, data["shape"], "FillColour.Set") if shape is None: return shape.fill_colour = data["value"] shape.save() await sio.emit( "Shape.Options.FillColour.Set", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def set_name_visible(sid: str, data: ShapeSetBooleanValue): pr: PlayerRoom = game_state.get(sid) shape = get_shape_or_none(pr, data["shape"], "NameVisible.Set") if shape is None: return shape.name_visible = data["value"] shape.save() await sio.emit( "Shape.Options.NameVisible.Set", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def update_shape_owner(sid: str, data: ServerShapeOwner): pr: PlayerRoom = game_state.get(sid) try: shape = Shape.get(uuid=data["shape"]) except Shape.DoesNotExist as exc: logger.warning( f"Attempt to update owner of unknown shape by {pr.player.name} [{data['shape']}]" ) raise exc if not has_ownership(shape, pr): logger.warning( f"{pr.player.name} attempted to change asset ownership of a shape it does not own" ) return target_user = User.by_name(data["user"]) if target_user is None: logger.warning( f"Attempt to update unknown user as owner to shape by {pr.player.name} [{data['user']}]" ) return try: so = ShapeOwner.get(shape=shape, user=target_user) except ShapeOwner.DoesNotExist as exc: logger.warning( f"Attempt to update unknown shape-owner relation by {pr.player.name}" ) so.shape = shape so.user = target_user so.edit_access = data["edit_access"] so.movement_access = data["movement_access"] so.vision_access = data["vision_access"] so.save() await sio.emit( "Shape.Owner.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def update_shape_tracker(sid: str, data: TrackerUpdateData): pr: PlayerRoom = game_state.get(sid) if data["_type"] == "tracker": tracker = Tracker.get_by_id(data["uuid"]) else: tracker = Aura.get_by_id(data["uuid"]) tracker.value = data["value"] tracker.save() await sio.emit( "Shapes.Trackers.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def rename_location(sid: int, data: Dict[str, str]): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to rename a location.") return location = Location[data["id"]] location.name = data["new"] location.save() for player_room in pr.room.players: for psid in game_state.get_sids(skip_sid=sid, player=player_room.player): await sio.emit("Location.Rename", data, room=psid, namespace="/planarally")
async def unarchive_location(sid: str, location_id: int): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to unarchive a location.") return location = Location.get_by_id(location_id) location.archived = False location.save() for player_room in pr.room.players: for psid in game_state.get_sids(skip_sid=sid, player=player_room.player): await sio.emit("Location.Unarchive", location_id, room=psid, namespace=GAME_NS)
async def rename_location(sid: str, data: LocationRenameData): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to rename a location.") return location = Location.get_by_id(data["location"]) location.name = data["name"] location.save() for player_room in pr.room.players: for psid in game_state.get_sids(skip_sid=sid, player=player_room.player): await sio.emit("Location.Rename", data, room=psid, namespace=GAME_NS)
async def set_floor_background(sid: str, data: FloorBackgroundData): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to set floor background") return floor: Floor = Floor.get(location=pr.active_location, name=data["name"]) floor.background_color = data.get("background", None) floor.save() await sio.emit( "Floor.Background.Set", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def create_group(sid: str, group_info: ServerGroup): pr: PlayerRoom = game_state.get(sid) try: Group.get_by_id(group_info["uuid"]) logger.exception(f"Group with {group_info['uuid']} already exists") return except Group.DoesNotExist: Group.create(**group_info) for psid, _ in game_state.get_users(room=pr.room): await sio.emit( "Group.Create", group_info, room=psid, skip_sid=sid, namespace=GAME_NS, )
async def set_floor_type(sid: str, data: FloorTypeData): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to set floor type") return floor: Floor = Floor.get(location=pr.active_location, name=data["name"]) floor.type_ = data["floorType"] floor.save() await sio.emit( "Floor.Type.Set", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def rename_floor(sid: str, data: FloorRename): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to rename a floor") return floor: Floor = Floor.get(location=pr.active_location, index=data["index"]) floor.name = data["name"] floor.save() await sio.emit( "Floor.Rename", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def set_floor_visibility(sid: str, data: FloorVisibleData): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning( f"{pr.player.name} attempted to toggle floor visibility") return floor: Floor = Floor.get(location=pr.active_location, name=data["name"]) floor.player_visible = data["visible"] floor.save() await sio.emit( "Floor.Visible.Set", data, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def update_note(sid: str, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) note = Note.get_or_none(uuid=data["uuid"]) if not note: logger.warning( f"{pr.player.name} tried to update non-existant note with id: '{data['uuid']}'" ) return if note.user != pr.player: logger.warn(f"{pr.player.name} tried to update note not belonging to him/her.") else: with db.atomic(): note.title = data["title"] note.text = data["text"] note.save()
async def set_location_options(sid: str, data: LocationOptionsData): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to set a room option") return if data.get("location", None) is None: options = pr.room.default_options else: loc = Location.get_by_id(data["location"]) if loc.options is None: loc.options = LocationOptions.create( unit_size=None, unit_size_unit=None, grid_type=None, use_grid=None, full_fow=None, fow_opacity=None, fow_los=None, vision_mode=None, vision_min_range=None, vision_max_range=None, ) loc.save() options = loc.options update_model_from_dict(options, data["options"]) options.save() if data.get("location", None) is None: for sid in game_state.get_sids(skip_sid=sid, room=pr.room): await sio.emit("Location.Options.Set", data, room=sid, namespace=GAME_NS) else: await sio.emit( "Location.Options.Set", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def move_aura(sid: str, data: AuraMove): pr: PlayerRoom = game_state.get(sid) new_shape = get_shape_or_none(pr, data["new_shape"], "Aura.Options.Tracker.Move") if new_shape is None: return aura = Aura.get_by_id(data["aura"]) aura.shape = new_shape aura.save() await sio.emit( "Shape.Options.Aura.Move", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def remove_group(sid: str, group_id: str): pr: PlayerRoom = game_state.get(sid) for shape in Shape.filter(group_id=group_id).select(): shape.group = None shape.show_badge = False shape.save() # check if group still has members await remove_group_if_empty(group_id) for psid, _ in game_state.get_users(room=pr.room): await sio.emit( "Group.Remove", group_id, room=psid, skip_sid=sid, namespace=GAME_NS, )
async def update_circle_size(sid: str, data: CircleSizeData): pr: PlayerRoom = game_state.get(sid) if not data["temporary"]: shape: Union[Circle, CircularToken] try: shape = CircularToken.get_by_id(data["uuid"]) except CircularToken.DoesNotExist: shape = Circle.get_by_id(data["uuid"]) shape.radius = data["r"] shape.save() await sio.emit( "Shape.Circle.Size.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def move_tracker(sid: str, data: TrackerMove): pr: PlayerRoom = game_state.get(sid) new_shape = get_shape_or_none(pr, data["new_shape"], "Tracker.Options.Tracker.Move") if new_shape is None: return tracker = Tracker.get_by_id(data["tracker"]) tracker.shape = new_shape tracker.save() await sio.emit( "Shape.Options.Tracker.Move", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def add_new_location(sid: str, location: str): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to add a new location") return new_location = Location.create(room=pr.room, name=location, index=pr.room.locations.count()) new_location.create_floor() for psid in game_state.get_sids(player=pr.player, active_location=pr.active_location): sio.leave_room(psid, pr.active_location.get_path(), namespace=GAME_NS) sio.enter_room(psid, new_location.get_path(), namespace=GAME_NS) await load_location(psid, new_location) pr.active_location = new_location pr.save()
async def set_locations_order(sid: int, locations: List[int]): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to reorder locations.") return for i, idx in enumerate(locations): l: Location = Location[idx] l.index = i + 1 l.save() for player_room in pr.room.players: if player_room.role != Role.DM: continue for psid in game_state.get_sids(skip_sid=sid, player=player_room.player): await sio.emit( "Locations.Order.Set", locations, room=psid, namespace=GAME_NS )
async def set_visibility(sid: str, data: LabelVisibilityMessage): pr: PlayerRoom = game_state.get(sid) label = Label.get_or_none(uuid=data["uuid"]) if label is None: logger.warn(f"{pr.player.name} tried to change a non-existing label.") return if label.user != pr.player: logger.warn(f"{pr.player.name} tried to change another user's label.") return label.visible = data["visible"] label.save() for psid in game_state.get_sids(skip_sid=sid, room=pr.room): if game_state.get_user(psid) == pr.player: await sio.emit( "Label.Visibility.Set", { "user": label.pr.player.name, **data }, room=psid, namespace=GAME_NS, ) else: if data["visible"]: await sio.emit("Label.Add", label.as_dict(), room=psid, namespace=GAME_NS) else: await sio.emit( "Label.Delete", { "uuid": label.uuid, "user": label.user.name }, room=psid, namespace=GAME_NS, )
async def update_default_shape_owner(sid: int, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) try: shape: Shape = Shape.get(uuid=data["shape"]) except Shape.DoesNotExist as exc: logger.warning( f"Attempt to update owner of unknown shape by {pr.player.name} [{data['shape']}]" ) raise exc if not has_ownership(shape, pr): logger.warning( f"{pr.player.name} attempted to change asset ownership of a shape it does not own" ) return if "edit_access" in data: shape.default_edit_access = data["edit_access"] if "vision_access" in data: shape.default_vision_access = data["vision_access"] shape.save() await sio.emit( "Shape.Owner.Default.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace="/planarally", ) if shape.default_vision_access or shape.default_edit_access: for sid, player in game_state.get_users( active_location=pr.active_location): await sio.emit( "Shape.Set", shape.as_dict(player, player.name == pr.room.creator), room=sid, namespace="/planarally", )
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 update_initiative_round(sid: int, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning( f"{pr.player.name} attempted to advance the initiative tracker") return location_data = InitiativeLocationData.get(location=pr.active_location) with db.atomic(): location_data.round = data location_data.save() await sio.emit( "Initiative.Round.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def get_location_spawn_info(sid: int, location_id: int): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning( f"{pr.player.name} attempted to retrieve spawn locations.") return location = Location[location_id] data = [] for spawn in json.loads(location.options.spawn_locations): shape = Shape[spawn] data.append(shape.as_dict(pr.player, True)) await sio.emit("Location.Spawn.Info", data=data, room=sid, namespace=GAME_NS)
async def kick_player(sid: str, player_id: int): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to refresh the invitation code.") return pr = PlayerRoom.get_or_none(player=player_id, room=pr.room) if pr is None: return creator: User = pr.room.creator if pr.player != creator and creator == pr.player: logger.warning(f"{pr.player.name} attempted to kick the campaign creator") return for psid in game_state.get_sids(player=pr.player, room=pr.room): await sio.disconnect(psid, namespace=GAME_NS) pr.delete_instance(True)