async def new_initiative_effect(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] if room.creator != user and not ShapeOwner.get_or_none(shape=shape, user=user): logger.warning( f"{user.name} attempted to create a new initiative effect") return InitiativeEffect.create( initiative=data["actor"], uuid=data["effect"]["uuid"], name=data["effect"]["name"], turns=data["effect"]["turns"], ) await sio.emit( "Initiative.Effect.New", data, room=location.get_path(), skip_sid=sid, namespace="/planarally", )
async def remove_shape(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] # We're first gonna retrieve the existing server side shape for some validation checks if data["temporary"]: # This stuff is not stored so we cannot do any server side validation /shrug shape = data["shape"] layer = location.layers.where(Layer.name == data["shape"]["layer"])[0] else: # Use the server version of the shape. try: shape = Shape.get(uuid=data["shape"]["uuid"]) except Shape.DoesNotExist: logger.warning(f"Attempt to update unknown shape by {user.name}") return layer = shape.layer # Ownership validatation if room.creator != user: if not layer.player_editable: logger.warning( f"{user.name} attempted to remove a shape on a dm layer") return if data["temporary"]: if user.name not in shape["owners"]: logger.warning( f"{user.name} attempted to remove asset it does not own") return else: if not ShapeOwner.get_or_none(shape=shape, user=user): logger.warning( f"{user.name} attempted to remove asset it does not own") return if data["temporary"]: state.remove_temp(sid, data["shape"]["uuid"]) else: shape.delete_instance() if layer.player_visible: await sio.emit( "Shape.Remove", data["shape"], room=location.get_path(), skip_sid=sid, namespace="/planarally", ) else: for csid in state.get_sids(user=room.creator, room=room): if csid == sid: continue await sio.emit("Shape.Remove", data["shape"], room=csid, namespace="/planarally")
async def add_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 add owner to 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 add unknown user as owner to shape by {pr.player.name} [{data['user']}]" ) return # Adding the DM as user is redundant and can only lead to confusion if target_user == pr.room.creator: return if not ShapeOwner.get_or_none(shape=shape, user=target_user): ShapeOwner.create( shape=shape, user=target_user, edit_access=data["edit_access"], movement_access=data["movement_access"], vision_access=data["vision_access"], ) await send_client_initiatives(pr, target_user) await sio.emit( "Shape.Owner.Add", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, ) if not (shape.default_vision_access or shape.default_edit_access): for sid in game_state.get_sids( player=target_user, active_location=pr.active_location ): await sio.emit( "Shape.Set", shape.as_dict(target_user, False), room=sid, namespace=GAME_NS, )
async def update_shape_owner(sid: int, data: Dict[str, Any]): 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 ShapeOwner.get_or_none(shape=shape, user=pr.player): 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.vision_access = data["vision_access"] so.save() await sio.emit( "Shape.Owner.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace="/planarally", )
async def has_ownership(layer, room, data, user, shape): # Ownership validatation if room.creator != user: if not layer.player_editable: logger.warning( f"{user.name} attempted to move a shape on a dm layer") return False if data["temporary"]: if user.name not in shape["owners"]: logger.warning( f"{user.name} attempted to move asset it does not own") return False else: if not ShapeOwner.get_or_none(shape=shape, user=user): logger.warning( f"{user.name} attempted to move asset it does not own") return False return True
async def update_initiative_effect(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] if room.creator != user and not ShapeOwner.get_or_none(shape=shape, user=user): logger.warning(f"{user.name} attempted to update an initiative effect") return with db.atomic(): effect = InitiativeEffect.get(uuid=data["effect"]["uuid"]) update_model_from_dict( effect, reduce_data_to_model(InitiativeEffect, data["effect"])) effect.save() await sio.emit( "Initiative.Effect.Update", data, room=location.get_path(), skip_sid=sid, namespace="/planarally", )
async def update_shape(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] # We're first gonna retrieve the existing server side shape for some validation checks if data["temporary"]: # This stuff is not stored so we cannot do any server side validation /shrug shape = data["shape"] layer = location.layers.where(Layer.name == data["shape"]["layer"])[0] else: # Use the server version of the shape. try: shape = Shape.get(uuid=data["shape"]["uuid"]) except Shape.DoesNotExist: logger.warning(f"Attempt to update unknown shape by {user.name}") return layer = shape.layer # Ownership validatation if room.creator != user: if not layer.player_editable: logger.warning( f"{user.name} attempted to move a shape on a dm layer") return if data["temporary"]: if user.name not in shape["owners"]: logger.warning( f"{user.name} attempted to move asset it does not own") return else: if not ShapeOwner.get_or_none(shape=shape, user=user): logger.warning( f"{user.name} attempted to move asset it does not own") return # Overwrite the old data with the new data if not data["temporary"]: with db.atomic(): data["shape"]["layer"] = Layer.get(location=location, name=data["shape"]["layer"]) # Otherwise backrefs can cause errors as they need to be handled separately update_model_from_dict(shape, reduce_data_to_model(Shape, data["shape"])) shape.save() type_table = get_table(shape.type_) type_instance = type_table.get(uuid=shape.uuid) # no backrefs on these tables update_model_from_dict(type_instance, data["shape"], ignore_unknown=True) type_instance.save() old_owners = {owner.user.name for owner in shape.owners} new_owners = set(data["shape"]["owners"]) for owner in old_owners ^ new_owners: if owner == "": continue delta_owner = User.by_name(owner) if owner in new_owners: ShapeOwner.create(shape=shape, user=delta_owner) else: ShapeOwner.get(shape=shape, user=delta_owner).delete_instance(True) await send_client_initiatives(room, location, delta_owner) # Send to players if layer.player_visible: for room_player in room.players: for psid in state.get_sids(user=room_player.player, room=room): if psid == sid: continue if not data["temporary"]: data["shape"] = shape.as_dict(room_player.player, False) await sio.emit("Shape.Update", data, room=psid, namespace="/planarally") # Send to DM for csid in state.get_sids(user=room.creator, room=room): if csid == sid: continue if not data["temporary"]: data["shape"] = shape.as_dict(room.creator, True) await sio.emit("Shape.Update", data, room=csid, namespace="/planarally")
async def update_initiative(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] shape = Shape.get_or_none(uuid=data["uuid"]) owner = ShapeOwner.get_or_none(shape=shape, user=user) is not None if room.creator != user and not owner: logger.warning( f"{user.name} attempted to change initiative of an asset it does not own" ) return used_to_be_visible = False location_data = InitiativeLocationData.get_or_none(location=location) if location_data is None: location_data = InitiativeLocationData.create(location=location, turn=data["uuid"], round=1) initiatives = Initiative.select().where( Initiative.location_data == location_data) initiative = Initiative.get_or_none(uuid=data["uuid"]) # Create new initiative if initiative is None: with db.atomic(): # Update indices try: index = (initiatives.where( Initiative.initiative >= data["initiative"]).order_by( -Initiative.index)[0].index + 1) except IndexError: index = 0 else: Initiative.update( index=Initiative.index + 1).where((Initiative.location_data == location_data) & (Initiative.index >= index)) # Create model instance initiative = dict_to_model(Initiative, reduce_data_to_model(Initiative, data)) initiative.location_data = location_data initiative.index = index initiative.save(force_insert=True) # Remove initiative elif "initiative" not in data: with db.atomic(): Initiative.update( index=Initiative.index - 1).where((Initiative.location_data == location_data) & (Initiative.index >= initiative.index)) initiative.delete_instance(True) # Update initiative else: used_to_be_visible = initiative.visible with db.atomic(): if data["initiative"] != initiative.initiative: # Update indices old_index = initiative.index try: new_index = (initiatives.where( Initiative.initiative >= data["initiative"]).order_by( -Initiative.index)[0].index) except IndexError: new_index = 0 else: if new_index < old_index: new_index += 1 if old_index != new_index: # SIGN=1 IF old_index > new_index WHICH MEANS the initiative is increased # SIGN=-1 IF old_index < new_index WHICH MEANS the initiative is decreased sign = (old_index - new_index) // abs(old_index - new_index) indices = [0, old_index, new_index] update = Initiative.update( index=Initiative.index + sign).where((Initiative.location_data == location_data) & (Initiative.index <= indices[sign]) & (Initiative.index >= indices[-sign])) update.execute() data["index"] = new_index # Update model instance update_model_from_dict(initiative, reduce_data_to_model(Initiative, data)) initiative.save() data["index"] = initiative.index await send_client_initiatives(room, location)