async def update_shape_position(sid: str, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) if data["temporary"] and not has_ownership_temp(data["shape"], pr): logger.warning( f"User {pr.player.name} attempted to move a shape it does not own." ) return shape, layer = await _get_shape(data, pr) # Overwrite the old data with the new data if not data["temporary"]: if not has_ownership(shape, pr): logger.warning( f"User {pr.player.name} attempted to move a shape it does not own." ) return with db.atomic(): # Shape update_model_from_dict(shape, reduce_data_to_model(Shape, data["shape"])) shape.save() if shape.type_ == "polygon": # Subshape type_instance = shape.subtype # no backrefs on these tables type_instance.update_from_dict(data["shape"], ignore_unknown=True) type_instance.save() await sync_shape_update(layer, pr, data, sid, shape)
async def update_initiative_option(sid: str, data: ServerInitiativeOption): pr: PlayerRoom = game_state.get(sid) shape = Shape.get_or_none(uuid=data["shape"]) if not has_ownership(shape, pr): logger.warning( f"{pr.player.name} attempted to change initiative of an asset it does not own" ) return location_data = Initiative.get_or_none(location=pr.active_location) if location_data is None: logger.error( "Initiative updated for location without initiative tracking") return json_data = json.loads(location_data.data) with db.atomic(): for i, initiative_data in enumerate(json_data): if initiative_data["shape"] == data["shape"]: json_data[i][data["option"]] = data["value"] break location_data.data = json.dumps(json_data) location_data.save() await sio.emit( "Initiative.Option.Set", data, skip_sid=sid, room=pr.active_location.get_path(), namespace=GAME_NS, )
async def change_initiative_order(sid: str, data: ServerInitiativeOrderChange): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to reorder initiatives") return old_index = data["oldIndex"] new_index = data["newIndex"] with db.atomic(): location_data = Initiative.get(location=pr.active_location) json_data = json.loads(location_data.data) if json_data[old_index]["shape"] != data["shape"]: return if json_data[new_index].get( "initiative", 0) != json_data[old_index].get("initiative", 0): location_data.sort = 2 json_data.insert(new_index, json_data.pop(old_index)) json_data = sort_initiative(json_data, location_data.sort) location_data.data = json.dumps(json_data) location_data.save() await sio.emit( "Initiative.Set", location_data.as_dict(), room=pr.active_location.get_path(), namespace=GAME_NS, )
async def create(request): allow_new_sessions = config.getboolean("Access control", "allow_new_sessions") if not allow_new_sessions: return web.HTTPConflict( reason="This feature is disabled by administrator") user = await check_authorized(request) data = await request.json() roomname = data["name"] if not roomname: return web.HTTPBadRequest() else: with db.atomic(): default_options = LocationOptions.create() room = Room.create(name=roomname, creator=user, default_options=default_options) loc = Location.create(room=room, name="start", index=1) loc.create_floor() PlayerRoom.create(player=user, room=room, role=Role.DM, active_location=loc) room.save() return web.HTTPOk()
async def create(request: web.Request): user: User = await check_authorized(request) data = await request.json() roomname = data["name"] logo = data["logo"] if not roomname: return web.HTTPBadRequest() else: if Room.get_or_none(name=roomname, creator=user): return web.HTTPConflict() with db.atomic(): default_options = LocationOptions.create() room = Room.create( name=roomname, creator=user, default_options=default_options, ) if logo >= 0: room.logo_id = logo loc = Location.create(room=room, name="start", index=1) loc.create_floor() PlayerRoom.create(player=user, room=room, role=Role.DM, active_location=loc) room.save() return web.HTTPOk()
async def update_shape_position(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] shape, layer = await _get_shape(data, location, user) if not await has_ownership(layer, room, data, user, shape): 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"]) # Shape model = reduce_data_to_model(Shape, data["shape"]) update_model_from_dict(shape, model) shape.save() if shape.type_ == "polygon": # Subshape type_instance = shape.subtype # no backrefs on these tables type_instance.update_from_dict(data["shape"], ignore_unknown=True) type_instance.save() await sync_shape_update(layer, room, data, sid, shape)
async def add_shape(sid: int, data: Dict[str, Any]): pr: PlayerRoom = game_state.get(sid) if "temporary" not in data: data["temporary"] = False floor = pr.active_location.floors.select().where( Floor.name == data["shape"]["floor"])[0] layer = floor.layers.where(Layer.name == data["shape"]["layer"])[0] if pr.role != Role.DM and not layer.player_editable: logger.warning( f"{pr.player.name} attempted to add a shape to a dm layer") return if data["temporary"]: game_state.add_temp(sid, data["shape"]["uuid"]) else: with db.atomic(): data["shape"]["layer"] = layer data["shape"]["index"] = layer.shapes.count() # Shape itself shape = Shape.create(**reduce_data_to_model(Shape, data["shape"])) # Subshape type_table = get_table(shape.type_) type_table.create( shape=shape, **type_table.pre_create( **reduce_data_to_model(type_table, data["shape"])), ) # Owners for owner in data["shape"]["owners"]: ShapeOwner.create( shape=shape, user=User.by_name(owner["user"]), edit_access=owner["edit_access"], movement_access=owner["movement_access"], vision_access=owner["vision_access"], ) # Trackers for tracker in data["shape"]["trackers"]: Tracker.create(**reduce_data_to_model(Tracker, tracker), shape=shape) # Auras for aura in data["shape"]["auras"]: Aura.create(**reduce_data_to_model(Aura, aura), shape=shape) for room_player in pr.room.players: is_dm = room_player.role == Role.DM for psid in game_state.get_sids(player=room_player.player, active_location=pr.active_location): if psid == sid: continue if not is_dm and not layer.player_visible: continue if not data["temporary"]: data["shape"] = shape.as_dict(room_player.player, is_dm) await sio.emit("Shape.Add", data["shape"], room=psid, namespace=GAME_NS)
async def update_initiative_turn(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.turn = data location_data.save() effects = (InitiativeEffect.select().join(Initiative).where( Initiative.uuid == data)) for effect in effects: if effect.turns <= 0: effect.delete_instance() else: effect.turns -= 1 effect.save() await sio.emit( "Initiative.Turn.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def register(request): username = await authorized_userid(request) if username: return web.HTTPOk() data = await request.json() username = data["username"] password = data["password"] email = data.get("email", None) if User.by_name(username): return web.HTTPConflict(reason="Username already taken") elif not username: return web.HTTPBadRequest(reason="Please provide a username") elif not password: return web.HTTPBadRequest(reason="Please provide a password") else: try: with db.atomic(): u = User(name=username) u.set_password(password) if email: u.email = email u.save() except: return web.HTTPServerError( reason= "An unexpected error occured on the server during account creation. Operation reverted." ) response = web.HTTPOk() await remember(request, response, username) return response
async def update_initiative_round(sid: str, data: int): pr: PlayerRoom = game_state.get(sid) location_data = Initiative.get(location=pr.active_location) if pr.role != Role.DM: json_data = json.loads(location_data.data) if not has_ownership( Shape.get_or_none(uuid=json_data[location_data.turn]["shape"]), pr): logger.warning( f"{pr.player.name} attempted to advance the initiative tracker" ) return 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 register(request): allow_registration = config.getboolean("Access control", "allow_registration") if not allow_registration: return web.HTTPConflict( reason="This feature is disabled by administrator") username = await authorized_userid(request) if username: return web.HTTPOk() data = await request.json() username = data["username"] password = data["password"] if User.by_name(username): return web.HTTPConflict(reason="Username already taken") elif not username: return web.HTTPBadRequest(reason="Please provide a username") elif not password: return web.HTTPBadRequest(reason="Please provide a password") else: with db.atomic(): u = User(name=username) u.set_password(password) u.save() response = web.HTTPOk() await remember(request, response, username) return response
async def update_shape_positions(sid: str, data: PositionUpdateList): pr: PlayerRoom = game_state.get(sid) shapes: List[Tuple[Shape, PositionUpdate]] = [] for sh in data["shapes"]: shape = Shape.get_or_none(Shape.uuid == sh["uuid"]) if shape is not None and not has_ownership(shape, pr, movement=True): logger.warning( f"User {pr.player.name} attempted to move a shape it does not own." ) return shapes.append((shape, sh)) if not data["temporary"]: with db.atomic(): for db_shape, data_shape in shapes: points = data_shape["position"]["points"] db_shape.x = points[0][0] db_shape.y = points[0][1] db_shape.angle = data_shape["position"]["angle"] db_shape.save() if len(points) > 1: # Subshape type_instance = db_shape.subtype type_instance.set_location(points[1:]) await sio.emit( "Shapes.Position.Update", data["shapes"], room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def update_shape_options(sid: str, data: OptionUpdateList): pr: PlayerRoom = game_state.get(sid) shapes: List[Tuple[Shape, OptionUpdate]] = [] for sh in data["options"]: shape = Shape.get_or_none(Shape.uuid == sh["uuid"]) if shape is not None and not has_ownership(shape, pr, movement=True): logger.warning( f"User {pr.player.name} attempted to change options for a shape it does not own." ) return shapes.append((shape, sh)) if not data["temporary"]: with db.atomic(): for db_shape, data_shape in shapes: db_shape.options = data_shape["option"] db_shape.save() await sio.emit( "Shapes.Options.Update", data["options"], room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def update_initiative_turn(sid, data): 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 advance the initiative tracker") return location_data = InitiativeLocationData.get(location=location) with db.atomic(): location_data.turn = data location_data.save() effects = (InitiativeEffect.select().join(Initiative).where( Initiative.uuid == data)) for effect in effects: if effect.turns <= 0: effect.delete_instance() else: effect.turns -= 1 effect.save() await sio.emit( "Initiative.Turn.Update", data, room=location.get_path(), skip_sid=sid, namespace="/planarally", )
async def set_initiative_sort(sid: str, sort: int): pr: PlayerRoom = game_state.get(sid) if pr.role != Role.DM: logger.warning(f"{pr.player.name} attempted to change initiative sort") return with db.atomic(): location_data = Initiative.get(location=pr.active_location) location_data.sort = sort json_data = json.loads(location_data.data) json_data = sort_initiative(json_data, location_data.sort) location_data.data = json.dumps(json_data) location_data.save() await sio.emit( "Initiative.Sort.Set", room=pr.active_location.get_path(), namespace=GAME_NS, ) await sio.emit( "Initiative.Set", location_data.as_dict(), room=pr.active_location.get_path(), namespace=GAME_NS, )
async def remove_initiative_effect(sid: str, data: ServerRemoveInitiativeEffectActor): pr: PlayerRoom = game_state.get(sid) if not has_ownership(Shape.get_or_none(uuid=data["shape"]), pr): logger.warning( f"{pr.player.name} attempted to remove an initiative effect") return location_data = Initiative.get(location=pr.active_location) with db.atomic(): json_data = json.loads(location_data.data) for initiative in json_data: if initiative["shape"] == data["shape"]: initiative["effects"].pop(data["index"]) location_data.data = json.dumps(json_data) location_data.save() await sio.emit( "Initiative.Effect.Remove", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def remove_initiative(sid: str, data: str): pr: PlayerRoom = game_state.get(sid) shape = Shape.get_or_none(uuid=data) if shape is not None and not has_ownership(shape, pr): logger.warning( f"{pr.player.name} attempted to remove initiative of an asset it does not own" ) return with db.atomic(): location_data = Initiative.get(location=pr.active_location) json_data = json.loads(location_data.data) location_data.data = json.dumps([ initiative for initiative in json_data if initiative["shape"] != data ]) location_data.save() await sio.emit( "Initiative.Remove", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def add_shape(sid, data): sid_data = state.sid_map[sid] user = sid_data["user"] room = sid_data["room"] location = sid_data["location"] if "temporary" not in data: data["temporary"] = False floor = location.floors.select().where( Floor.name == data["shape"]["floor"])[0] layer = floor.layers.where(Layer.name == data["shape"]["layer"])[0] if room.creator != user and not layer.player_editable: logger.warning(f"{user.name} attempted to add a shape to a dm layer") return if data["temporary"]: state.add_temp(sid, data["shape"]["uuid"]) else: with db.atomic(): data["shape"]["layer"] = layer data["shape"]["index"] = layer.shapes.count() # Shape itself shape = Shape.create(**reduce_data_to_model(Shape, data["shape"])) # Subshape type_table = get_table(shape.type_) type_table.create(shape=shape, **reduce_data_to_model(type_table, data["shape"])) # Owners ShapeOwner.create(shape=shape, user=user) # Trackers for tracker in data["shape"]["trackers"]: Tracker.create(**reduce_data_to_model(Tracker, tracker), shape=shape) # Auras for aura in data["shape"]["auras"]: Aura.create(**reduce_data_to_model(Aura, aura), shape=shape) 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.Add", data["shape"], room=psid, namespace="/planarally") 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.Add", data["shape"], room=csid, namespace="/planarally")
async def set_initiative_value(sid: str, data: ServerSetInitiativeValue): pr: PlayerRoom = game_state.get(sid) shape = Shape.get_or_none(uuid=data) if shape is not None and not has_ownership(shape, pr): logger.warning( f"{pr.player.name} attempted to remove initiative of an asset it does not own" ) return with db.atomic(): location_data = Initiative.get(location=pr.active_location) json_data = json.loads(location_data.data) for initiative in json_data: if initiative["shape"] == data["shape"]: initiative["initiative"] = data["value"] break json_data = sort_initiative(json_data, location_data.sort) location_data.data = json.dumps(json_data) location_data.save() await sio.emit( "Initiative.Set", location_data.as_dict(), room=pr.active_location.get_path(), namespace=GAME_NS, )
async def update_initiative_turn(sid: str, data: str): 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.turn = data location_data.save() effects = (InitiativeEffect.select().join(Initiative).where( Initiative.uuid == data)) for effect in effects: try: turns = int(effect.turns) if turns <= 0: effect.delete_instance() else: effect.turns = str(turns - 1) except ValueError: # For non-number inputs do not update the effect pass effect.save() await sio.emit( "Initiative.Turn.Update", data, room=pr.active_location.get_path(), skip_sid=sid, namespace=GAME_NS, )
async def set_client_room_options(sid: str, data: ClientOptions): pr: PlayerRoom = game_state.get(sid) with db.atomic(): if pr.user_options is None: pr.user_options = UserOptions.create_empty() pr.save() UserOptions.update(**data).where(UserOptions.id == pr.user_options).execute()
async def create(request): user = await check_authorized(request) data = await request.json() roomname = data["name"] if not roomname: return web.HTTPBadRequest() else: with db.atomic(): room = Room.create(name=roomname, creator=user) loc = Location.create(room=room, name="start") loc.create_floor() room.dm_location = loc.name room.player_location = loc.name room.save() return web.HTTPOk()
async def update_initiative_order(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 change the initiative order") return with db.atomic(): for i, uuid in enumerate(data): init = Initiative.get(uuid=uuid) init.index = i init.save() await send_client_initiatives(pr)
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 create_room(request): user = await check_authorized(request) data = await request.post() roomname = data["room_name"] if not roomname: response = web.HTTPFound("/rooms") else: with db.atomic(): room = Room.create(name=roomname, creator=user) loc = Location.create(room=room, name="start") loc.add_default_layers() room.dm_location = loc.name room.player_location = loc.name room.save() response = web.HTTPFound(f"/rooms/{user.name}/{roomname}") return response
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 update_initiative_order(sid, data): 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 change the initiative order") return location_data = InitiativeLocationData.get(location=location) with db.atomic(): for i, uuid in enumerate(data): init = Initiative.get(uuid=uuid) init.index = i init.save() await send_client_initiatives(room, location)
async def create(request): user = await check_authorized(request) data = await request.json() roomname = data["name"] if not roomname: return web.HTTPBadRequest() else: with db.atomic(): default_options = LocationOptions.create() room = Room.create(name=roomname, creator=user, default_options=default_options) loc = Location.create(room=room, name="start", index=1) loc.create_floor() PlayerRoom.create(player=user, room=room, role=Role.DM, active_location=loc) room.save() return web.HTTPOk()
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, )