Igloo handlers

This commit is contained in:
Ben 2019-10-09 01:22:18 +01:00
parent 889ce89620
commit c61e5d94df
2 changed files with 494 additions and 0 deletions

View File

@ -0,0 +1,491 @@
import itertools
import ujson
import time
from datetime import datetime, timedelta
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.converters import SeparatorConverter
from houdini.constants import ClientType
from houdini.handlers.play.navigation import handle_join_server
from houdini.data import db
from houdini.data.penguin import Penguin
from houdini.data.room import PenguinIglooRoom
from houdini.data.igloo import IglooFurniture, IglooLike, Igloo, Furniture, Flooring, Location
from sqlalchemy.dialects.postgresql import insert
from aiocache import cached
def get_layout_furniture_key(_, p, igloo_id):
return f'layout_furniture.{igloo_id}'
def get_active_igloo_string_key(_, p, penguin_id):
return f'active_igloo.{penguin_id}'
def get_legacy_igloo_string_key(_, p, penguin_id):
return f'legacy_igloo.{penguin_id}'
def get_igloo_layouts_key(_, p):
return f'igloo_layouts.{p.data.id}'
def get_layout_like_count_key(_, igloo_id):
return f'layout_like_count.{igloo_id}'
@cached(alias='default', key_builder=get_layout_furniture_key)
async def get_layout_furniture(p, igloo_id):
igloo_furniture = IglooFurniture.query.where(IglooFurniture.igloo_id == igloo_id).gino
async with p.server.db.transaction():
furniture_string = ','.join([f'{furniture.furniture_id}|{furniture.x}|{furniture.y}|'
f'{furniture.rotation}|{furniture.frame}'
async for furniture in igloo_furniture.iterate()])
return furniture_string
@cached(alias='default', key_builder=get_active_igloo_string_key)
async def get_active_igloo_string(p, penguin_id):
igloo = await PenguinIglooRoom.load(parent=Penguin.on(Penguin.igloo == PenguinIglooRoom.id))\
.where(PenguinIglooRoom.penguin_id == penguin_id).gino.first()
furniture_string = await get_layout_furniture(p, igloo.id)
like_count = await get_layout_like_count(igloo.id)
return f'{igloo.id}:1:0:{int(igloo.locked)}:{igloo.music}:{igloo.flooring}:' \
f'{igloo.location}:{igloo.type}:{like_count}:{furniture_string}'
@cached(alias='default', key_builder=get_legacy_igloo_string_key)
async def get_legacy_igloo_string(p, penguin_id):
igloo = await PenguinIglooRoom.load(parent=Penguin.on(Penguin.igloo == PenguinIglooRoom.id))\
.where(PenguinIglooRoom.penguin_id == penguin_id).gino.first()
furniture_string = await get_layout_furniture(p, igloo.id)
return f'{igloo.type}%{igloo.music}%{igloo.flooring}%{furniture_string}'
@cached(alias='default', key_builder=get_igloo_layouts_key)
async def get_all_igloo_layouts(p):
layout_details = []
slot = 0
for igloo in p.data.igloo_rooms.values():
slot += 1
furniture_string = await get_layout_furniture(p, igloo.id)
like_count = await get_layout_like_count(igloo.id)
igloo_details = f'{igloo.id}:{slot}:0:{int(igloo.locked)}:{igloo.music}:{igloo.flooring}' \
f':{igloo.location}:{igloo.type}:{like_count}:{furniture_string}'
layout_details.append(igloo_details)
return '%'.join(layout_details)
@cached(alias='default', key_builder=get_layout_like_count_key)
async def get_layout_like_count(igloo_id):
layout_like_count = await db.select([db.func.sum(IglooLike.count)])\
.where(IglooLike.igloo_id == igloo_id).gino.scalar()
return layout_like_count or 0
async def create_first_igloo(p, penguin_id):
igloo = await PenguinIglooRoom.query.where(PenguinIglooRoom.penguin_id == penguin_id).gino.scalar()
if igloo is None:
if penguin_id in p.server.penguins_by_id:
penguin = p.server.penguins_by_id[penguin_id]
igloo = await penguin.data.igloo_rooms.set(PenguinIglooRoom.id, penguin_id=penguin_id, type=1,
flooring=0, location=1)
await penguin.data.update(igloo=igloo.id).apply()
else:
igloo = await PenguinIglooRoom.create(penguin_id=penguin_id, type=1, flooring=0, location=1)
await Penguin.update.values(igloo=igloo.id)\
.where(Penguin.id == penguin_id).gino.status()
async def save_igloo_furniture(p, furniture_list=None):
await IglooFurniture.delete.where(IglooFurniture.igloo_id == p.data.igloo).gino.status()
if furniture_list:
furniture_tracker = {}
furniture = []
for furniture_string in itertools.islice(furniture_list, 0, 100):
furniture_id, x, y, rotation, frame = map(int, furniture_string.split('|'))
if furniture_id not in p.data.furniture:
return
if furniture_id not in furniture_tracker:
furniture_tracker[furniture_id] = 0
else:
furniture_tracker[furniture_id] += 1
if furniture_tracker[furniture_id] > p.data.furniture[furniture_id].quantity:
return
if not (0 <= x <= 700 and 0 <= y <= 700 and 1 <= rotation <= 8 and 1 <= frame <= 10):
return
furniture.append({
'igloo_id': p.data.igloo,
'furniture_id': furniture_id,
'x': x, 'y': y,
'frame': frame,
'rotation': rotation
})
await IglooFurniture.insert().values(furniture).gino.status()
@handlers.handler(XTPacket('g', 'gm'))
@handlers.cooldown(1)
async def handle_get_igloo_details(p, penguin_id: int):
await create_first_igloo(p, penguin_id)
igloo_string_method = get_active_igloo_string if p.is_vanilla_client else get_legacy_igloo_string
await p.send_xt('gm', penguin_id, await igloo_string_method(p, penguin_id))
@handlers.handler(XTPacket('g', 'gail'), client=ClientType.Vanilla)
async def handle_get_all_igloo_layouts(p):
await p.send_xt('gail', p.data.id, 0, await get_all_igloo_layouts(p))
@handlers.handler(XTPacket('g', 'ag'))
async def handle_buy_flooring(p, flooring: Flooring):
if flooring is None:
return await p.send_error(402)
if p.is_vanilla_client:
if flooring.id in p.data.flooring:
return await p.send_error(400)
if p.data.coins < flooring.cost:
return await p.send_error(401)
await p.add_flooring(flooring)
else:
igloo = p.data.igloo_rooms[p.data.igloo]
await igloo.update(flooring=flooring.id).apply()
await p.data.update(coins=p.data.coins - flooring.cost).apply()
await p.send_xt('ag', flooring.id, p.data.coins)
await p.server.cache.delete(f'active_igloo.{p.data.id}')
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
await p.server.cache.delete(f'igloo_layouts.{p.data.id}')
@handlers.handler(XTPacket('g', 'aloc'), client=ClientType.Vanilla)
async def handle_buy_igloo_location(p, location: Location):
if location is None:
return await p.send_error(402)
if location.id in p.data.locations:
return await p.send_error(400)
if p.data.coins < location.cost:
return await p.send_error(401)
await p.add_location(location)
@handlers.handler(XTPacket('g', 'au'))
async def handle_buy_igloo_type(p, igloo: Igloo):
if igloo is None:
return await p.send_error(402)
if igloo.id in p.data.igloos:
return await p.send_error(400)
if p.data.coins < igloo.cost:
return await p.send_error(401)
await p.add_igloo(igloo)
@handlers.handler(XTPacket('g', 'af'))
async def handle_buy_furniture(p, furniture: Furniture):
if furniture is None:
return await p.send_error(402)
if furniture.id in p.data.igloos:
return await p.send_error(400)
if p.data.coins < furniture.cost:
return await p.send_error(401)
await p.add_furniture(furniture)
@handlers.handler(XTPacket('g', 'uic'), client=ClientType.Vanilla)
@handlers.cooldown(1)
async def handle_update_igloo_configuration(p, igloo_id: int, igloo_type_id: int, flooring_id: int, location_id: int,
music_id: int, furniture_data):
if p.room.igloo and p.room.penguin_id == p.data.id and igloo_id in p.data.igloo_rooms:
igloo = p.data.igloo_rooms[igloo_id]
await p.data.update(igloo=igloo_id).apply()
p.server.igloos_by_penguin_id[p.data.id] = igloo
furniture_list = furniture_data.split(',') if furniture_data else None
await save_igloo_furniture(p, furniture_list)
if not igloo_type_id or igloo_type_id in p.data.igloos\
and not flooring_id or flooring_id in p.data.flooring\
and not location_id or location_id in p.data.locations:
await igloo.update(
type=igloo_type_id,
flooring=flooring_id,
location=location_id,
music=music_id
).apply()
like_count = await get_layout_like_count(igloo.id)
active_igloo_string = f'{igloo.id}:1:0:{int(igloo.locked)}:{igloo.music}:{igloo.flooring}:' \
f'{igloo.location}:{igloo.type}:{like_count}:{furniture_data}'
await p.room.send_xt('uvi', p.data.id, active_igloo_string)
await p.server.cache.set(f'layout_furniture.{igloo.id}', furniture_data)
await p.server.cache.set(f'active_igloo.{p.data.id}', active_igloo_string)
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
await p.server.cache.delete(f'igloo_layouts.{p.data.id}')
@handlers.handler(XTPacket('g', 'ur'), client=ClientType.Legacy)
@handlers.cooldown(1)
async def handle_save_igloo_furniture(p, *furniture_data):
await save_igloo_furniture(p, furniture_data)
await p.server.cache.set(f'layout_furniture.{p.data.igloo}', ','.join(furniture_data))
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
_slot_converter = SeparatorConverter(separator=',', mapper=str)
@handlers.handler(XTPacket('g', 'uiss'), client=ClientType.Vanilla)
@handlers.cooldown(1)
async def handle_update_igloo_slot_summary(p, igloo_id: int, slot_summary: _slot_converter):
if p.room.igloo and p.room.penguin_id == p.data.id and igloo_id in p.data.igloo_rooms:
igloo = p.data.igloo_rooms[igloo_id]
await p.data.update(igloo=igloo_id).apply()
p.server.igloos_by_penguin_id[p.data.id] = igloo
if p.data.id in p.server.open_igloos_by_penguin_id:
del p.server.open_igloos_by_penguin_id[p.data.id]
for slot in slot_summary:
igloo_id, locked = map(int, slot.split('|'))
igloo = p.data.igloo_rooms[igloo_id]
if igloo_id == p.data.igloo:
if not locked:
p.server.open_igloos_by_penguin_id[p.data.id] = igloo
if igloo.locked != bool(locked):
await igloo.update(locked=bool(locked)).apply()
await p.server.cache.delete(f'active_igloo.{p.data.id}')
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
await p.server.cache.delete(f'igloo_layouts.{p.data.id}')
active_igloo_string = await get_active_igloo_string(p, p.data.id)
await p.room.send_xt('uvi', p.data.id, active_igloo_string)
@handlers.handler(XTPacket('j', 'js'), after=handle_join_server, client=ClientType.Vanilla)
async def handle_add_igloo_map(p):
if p.data.igloo is not None:
igloo = p.data.igloo_rooms[p.data.igloo]
if not igloo.locked:
p.server.open_igloos_by_penguin_id[p.data.id] = igloo
@handlers.disconnected
async def handle_remove_igloo_map(p):
if p.data.id in p.server.open_igloos_by_penguin_id:
del p.server.open_igloos_by_penguin_id[p.data.id]
@handlers.handler(XTPacket('g', 'pio'), client=ClientType.Vanilla)
async def handle_is_player_igloo_open(p, penguin_id: int):
await p.send_xt('pio', int(penguin_id in p.server.open_igloos_by_penguin_id))
@handlers.handler(XTPacket('g', 'al'), client=ClientType.Vanilla)
async def handle_add_igloo_layout(p):
if len(p.data.igloo_rooms) < 4:
igloo = await p.data.igloo_rooms.set(PenguinIglooRoom.id, penguin_id=p.data.id, type=1, flooring=0, location=1)
slot_id = len(p.data.igloo_rooms)
await p.send_xt('al', p.data.id, f'{igloo.id}:{slot_id}:0:{int(igloo.locked)}:{igloo.music}:{igloo.flooring}:'
f'{igloo.location}:{igloo.type}:0:')
@handlers.handler(XTPacket('g', 'gili'), client=ClientType.Vanilla)
@handlers.cooldown(1)
async def handle_get_igloo_like_by(p, pagination_start: int, pagination_end: int):
if p.room.igloo:
like_count = await get_layout_like_count(p.room.id)
liked_by = IglooLike.query.where(IglooLike.igloo_id == p.room.id). \
limit(pagination_end - pagination_start).offset(pagination_start).gino
async with p.server.db.transaction():
like_collection = {
'likedby': {
'counts': {
'count': like_count,
'maxCount': like_count,
'accumCount': like_count
},
'IDs': [
{
'id': like.player_id,
'time': int(time.mktime(like.date.timetuple())),
'count': like.count,
'isFriend': like.player_id in p.data.buddies
} async for like in liked_by.iterate()
]
},
}
await p.send_xt('gili', p.room.id, 200, ujson.dumps(like_collection))
@handlers.handler(XTPacket('g', 'cli'), client=ClientType.Vanilla)
async def handle_can_like_igloo(p):
last_like = await db.select([IglooLike.date]).where((IglooLike.igloo_id == p.room.id)
& (IglooLike.player_id == p.data.id)).gino.scalar()
time_elapsed = datetime.now()
if last_like is not None:
time_elapsed = datetime.now() - last_like
can_like = ujson.dumps({'canLike': True, 'periodicity': 'ScheduleDaily', 'nextLike_msecs': 0})
if last_like is None or time_elapsed > timedelta(1):
await p.send_xt('cli', p.room.id, 200, can_like)
else:
next_like = int((timedelta(1) - time_elapsed).total_seconds() * 1000)
await p.send_xt('cli', p.room.id, 200, ujson.dumps({'canLike': False, 'periodicity': 'ScheduleDaily',
'nextLike_msecs': next_like}))
@handlers.handler(XTPacket('g', 'gr'), client=ClientType.Vanilla)
async def handle_get_open_igloo_list(p):
async def get_igloo_string(igloo):
owner_name = p.server.penguins_by_id[igloo.penguin_id].data.nickname
like_count = await get_layout_like_count(igloo.id)
igloo_population = len(igloo.penguins_by_id)
return f'{igloo.penguin_id}|{owner_name}|{like_count}|{igloo_population}|{int(igloo.locked)}'
open_igloos = [await get_igloo_string(igloo) for igloo in p.server.open_igloos_by_penguin_id.values()]
local_room_population = 0
own_layout_like_count = 0 if p.data.igloo is None else await get_layout_like_count(p.data.igloo)
await p.send_xt('gr', own_layout_like_count, local_room_population, *open_igloos)
@handlers.handler(XTPacket('g', 'gr'), client=ClientType.Legacy)
async def handle_get_open_igloo_list_legacy(p):
if not p.server.open_igloos_by_penguin_id:
return await p.send_line('%xt%gr%-1%')
async def get_igloo_string(igloo):
owner_name = p.server.penguins_by_id[igloo.penguin_id].data.nickname
return f'{igloo.penguin_id}|{owner_name}'
open_igloos = [await get_igloo_string(igloo) for igloo in p.server.open_igloos_by_penguin_id.values()]
await p.send_xt('gr', *open_igloos)
@handlers.handler(XTPacket('g', 'or'), client=ClientType.Legacy)
async def handle_unlock_igloo(p):
igloo = p.data.igloo_rooms[p.data.igloo]
p.server.open_igloos_by_penguin_id[p.data.id] = igloo
@handlers.handler(XTPacket('g', 'cr'), client=ClientType.Legacy)
async def handle_lock_igloo(p):
del p.server.open_igloos_by_penguin_id[p.data.id]
@handlers.handler(XTPacket('g', 'go'), client=ClientType.Legacy)
async def handle_get_owned_igloos(p):
await p.send_xt('go', '|'.join(str(igloo_id) for igloo_id in p.data.igloos.keys()))
@handlers.handler(XTPacket('g', 'gf'), client=ClientType.Legacy)
async def handle_get_furniture(p):
furniture_string = '%'.join(f'{furniture.furniture_id}|{furniture.quantity}'
for furniture in p.data.furniture.values())
await p.send_xt('gf', furniture_string)
@handlers.handler(XTPacket('g', 'um'), client=ClientType.Legacy)
async def handle_update_igloo_music(p, music_id: int):
if p.room.igloo and p.room.penguin_id == p.data.id and p.room.music != music_id:
await p.room.update(music=music_id).apply()
await p.server.cache.delete(f'active_igloo.{p.data.id}')
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
await p.server.cache.delete(f'igloo_layouts.{p.data.id}')
@handlers.handler(XTPacket('g', 'ao'), client=ClientType.Legacy)
async def handle_activate_igloo_type(p, igloo_type_id: int):
if p.room.igloo and p.room.penguin_id == p.data.id and p.room.type != igloo_type_id \
and igloo_type_id in p.data.igloos:
await p.room.update(type=igloo_type_id, flooring=0).apply()
await p.server.cache.delete(f'active_igloo.{p.data.id}')
await p.server.cache.delete(f'legacy_igloo.{p.data.id}')
await p.server.cache.delete(f'igloo_layouts.{p.data.id}')
@handlers.handler(XTPacket('g', 'grf'), client=ClientType.Vanilla)
async def handle_get_friends_igloo_list(p):
async def get_friend_igloo_string(penguin):
like_count = 0 if penguin.data.igloo is None else await get_layout_like_count(penguin.data.igloo)
return f'{penguin.data.id}|{like_count}'
friend_igloos = [await get_friend_igloo_string(penguin) for penguin in p.server.penguins_by_id.values()
if penguin.data.id in p.data.buddies]
await p.send_xt('grf', *friend_igloos)
@handlers.handler(XTPacket('g', 'li'), client=ClientType.Vanilla)
@handlers.cooldown(1)
async def handle_like_igloo(p):
if p.room.igloo:
like_insert = insert(IglooLike).values(igloo_id=p.room.id, player_id=p.data.id)
like_insert = like_insert.on_conflict_do_update(
constraint='igloo_like_pkey',
set_=dict(count=IglooLike.count + 1, date=datetime.now()),
where=(IglooLike.date < datetime.today())
)
await like_insert.gino.status()
await p.server.cache.delete(f'layout_like_count.{p.room.id}')
if len(p.room.penguins_by_id) > 1:
like_count = await get_layout_like_count(p.room.id)
for penguin in p.room.penguins_by_id.values():
if penguin.data.id != p.data.id:
await p.send_xt('lue', p.data.id, like_count)
@handlers.handler(XTPacket('g', 'gii'), client=ClientType.Vanilla)
async def handle_get_furniture_inventory(p):
furniture = ','.join(f'{furniture_id}|0000000000|{furniture_item.quantity}'
for furniture_id, furniture_item in p.data.furniture.items())
flooring = ','.join(f'{flooring_id}|0000000000' for flooring_id in p.data.flooring.keys())
igloos = ','.join(f'{igloo_id}|0000000000' for igloo_id in p.data.igloos.keys())
locations = ','.join(f'{location_id}|0000000000' for location_id in p.data.locations.keys())
await p.send_xt('gii', furniture, flooring, igloos, locations)

View File

@ -73,6 +73,9 @@ class Houdini:
self.penguins_by_username = {}
self.penguins_by_character_id = {}
self.igloos_by_penguin_id = {}
self.open_igloos_by_penguin_id = {}
self.xt_listeners = XTListenerManager(self)
self.xml_listeners = XMLListenerManager(self)
self.dummy_event_listeners = DummyEventListenerManager(self)