Merge remote-tracking branch 'origin/master'

This commit is contained in:
rsakeys 2020-03-26 23:36:11 +00:00
commit 923c31fbd1
66 changed files with 8176 additions and 7622 deletions

View File

@ -1,9 +1,9 @@
import argparse
import asyncio
import logging
import argparse
from houdini.constants import ClientType, ConflictResolution, Language
from houdini.houdini import Houdini
from houdini.constants import Language, ConflictResolution, ClientType
if __name__ == '__main__':
logger = logging.getLogger('houdini')

14002
houdini.sql

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,10 @@
import asyncio
import importlib
import logging
import pkgutil
from abc import ABC, abstractmethod
from collections import OrderedDict
from types import FunctionType
from abc import abstractmethod
import asyncio
import logging
import importlib
import pkgutil
def get_package_modules(package):
@ -37,6 +36,56 @@ class _AbstractManager(dict):
"""Loads entries from module"""
class ITable(ABC):
"""
All table game logic classes must implement this interface.
"""
@abstractmethod
def make_move(self, *args):
"""Tells logic a move has been made."""
@abstractmethod
def is_valid_move(self, *args):
"""Returns true if the move is valid."""
@abstractmethod
def get_string(self):
"""Returns string representation of the game."""
class IWaddle(ABC):
"""
All waddle game logic classes must implement this interface.
"""
@property
@abstractmethod
def room_id(self):
"""External ID of waddle game room."""
def __init__(self, waddle):
self.penguins = list(waddle.penguins)
self.seats = waddle.seats
async def start(self):
room_id = type(self).room_id
for penguin in self.penguins:
penguin.waddle = self
await penguin.join_room(penguin.server.rooms[room_id])
async def remove_penguin(self, p):
self.penguins.remove(p)
p.waddle = None
async def send_xt(self, *data):
for penguin in self.penguins:
await penguin.send_xt(*data)
def get_seat_id(self, p):
return self.penguins.index(p)
class PenguinStringCompiler(OrderedDict):
def __init__(self, *args, **kwargs):

View File

@ -1,10 +1,7 @@
import inspect
from houdini import handlers
from houdini import plugins
from houdini import _AbstractManager
from houdini import _AbstractManager, handlers, plugins
from houdini.constants import ConflictResolution
from houdini.converters import _ArgumentDeserializer, _listener

View File

@ -1,18 +1,15 @@
from abc import ABC
from abc import abstractmethod
import asyncio
import itertools
import inspect
import collections
import inspect
import itertools
from abc import ABC, abstractmethod
from houdini.cooldown import CooldownError
from houdini.data.room import Room
from houdini.data.igloo import Flooring, Furniture, Igloo, Location
from houdini.data.item import Item
from houdini.data.igloo import Igloo, Furniture, Flooring, Location
from houdini.data.pet import PenguinPuffle, Puffle
from houdini.data.room import Room
from houdini.data.stamp import Stamp
from houdini.data.pet import Puffle, PenguinPuffle
class ChecklistError(Exception):

View File

@ -1,6 +1,7 @@
from gino import Gino
from collections.abc import Mapping
from gino import Gino
db = Gino()

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class BuddyList(db.Model):

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class DanceSong(db.Model):

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
from functools import cached_property
class Flooring(db.Model):
@ -8,6 +8,8 @@ class Flooring(db.Model):
name = db.Column(db.String(50))
cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
legacy_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
vanilla_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
class Furniture(db.Model):
@ -20,6 +22,8 @@ class Furniture(db.Model):
cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
legacy_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
vanilla_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
bait = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
max_quantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("100"))
innocent = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
@ -33,6 +37,8 @@ class Igloo(db.Model):
name = db.Column(db.String(50), nullable=False)
cost = db.Column(db.SmallInteger, nullable=False, server_default=db.text("0"))
patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
legacy_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
vanilla_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
class IglooFurniture(db.Model):
@ -66,6 +72,8 @@ class Location(db.Model):
name = db.Column(db.String(50), nullable=False)
cost = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
legacy_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
vanilla_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
class PenguinIgloo(db.Model):

View File

@ -1,5 +1,7 @@
from houdini.data import db, AbstractDataCollection
from functools import cached_property
class Item(db.Model):
__tablename__ = 'item'
@ -10,6 +12,8 @@ class Item(db.Model):
member = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
bait = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
patched = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
legacy_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
vanilla_inventory = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
epf = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
tour = db.Column(db.Boolean, nullable=False, server_default=db.text("false"))
release_date = db.Column(db.Date, nullable=False, server_default=db.text("now()"))

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Postcard(db.Model):

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Ban(db.Model):

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Card(db.Model):
@ -14,6 +14,16 @@ class Card(db.Model):
description = db.Column(db.String(255), nullable=False, server_default=db.text("''::character varying"))
class CardStarterDeck(db.Model):
__tablename__ = 'card_starter_deck'
item_id = db.Column(db.ForeignKey('item.id', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
nullable=False, index=True)
card_id = db.Column(db.ForeignKey('card.id', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True,
nullable=False)
quantity = db.Column(db.SmallInteger, nullable=False, server_default=db.text("1"))
class PenguinCard(db.Model):
__tablename__ = 'penguin_card'
@ -30,6 +40,21 @@ class CardCollection(AbstractDataCollection):
__indexby__ = 'id'
__filterby__ = 'id'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.starter_decks = {}
def set_starter_decks(self, starter_deck_cards):
for card in starter_deck_cards:
starter_deck = self.starter_decks.get(card.item_id, [])
starter_deck.append((self.get(card.card_id), card.quantity))
self.starter_decks[card.item_id] = starter_deck
@property
def power_cards(self):
return [card for card in self.values() if card.power_id > 0]
class PenguinCardCollection(AbstractDataCollection):
__model__ = PenguinCard

View File

@ -1,9 +1,8 @@
from datetime import datetime
from functools import lru_cache
from houdini.data import db
from functools import lru_cache
class Penguin(db.Model):
__tablename__ = 'penguin'

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Permission(db.Model):

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Puffle(db.Model):

View File

@ -1,9 +1,4 @@
from houdini.games.sled import SledRacingLogic
from houdini.games.four import ConnectFourLogic
from houdini.games.mancala import MancalaLogic
from houdini.games.treasure import TreasureHuntLogic
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
def stealth_mod_filter(stealth_mod_id):
@ -199,12 +194,6 @@ class RoomTable(db.Model):
nullable=False)
game = db.Column(db.String(20), nullable=False)
GameClassMapping = {
'four': ConnectFourLogic,
'mancala': MancalaLogic,
'treasure': TreasureHuntLogic
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.penguins = []
@ -272,14 +261,13 @@ class RoomWaddle(db.Model):
seats = db.Column(db.SmallInteger, nullable=False, server_default=db.text("2"))
game = db.Column(db.String(20), nullable=False)
GameClassMapping = {
'sled': SledRacingLogic
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.temporary = kwargs.pop('temporary', False)
self.penguins = []
self.logic = None
self.room = None
super().__init__(*args, **kwargs)
async def add_penguin(self, p):
if not self.penguins:
@ -287,29 +275,35 @@ class RoomWaddle(db.Model):
seat_id = self.penguins.index(None)
self.penguins[seat_id] = p
await p.send_xt("jw", seat_id)
await p.room.send_xt("uw", self.id, seat_id, p.safe_name)
await p.send_xt('jw', seat_id)
await p.room.send_xt('uw', self.id, seat_id, p.safe_name)
p.waddle = self
if self.penguins.count(None) == 0:
game_instance = RoomWaddle.GameClassMapping[self.game](self)
game_instance = self.logic(self)
await game_instance.start()
await self.reset()
if self.temporary:
del p.server.rooms[self.room_id].waddles[self.id]
async def remove_penguin(self, p):
seat_id = self.get_seat_id(p)
self.penguins[seat_id] = None
await p.room.send_xt("uw", self.id, seat_id)
await p.room.send_xt('uw', self.id, seat_id)
p.waddle = None
if self.temporary and self.penguins.count(None) == self.seats:
del p.server.rooms[self.room_id].waddles[self.id]
async def reset(self):
for seat_id, penguin in enumerate(self.penguins):
if penguin:
self.penguins[seat_id] = None
await penguin.room.send_xt("uw", self.id, seat_id)
await penguin.room.send_xt('uw', self.id, seat_id)
def get_seat_id(self, p):
return self.penguins.index(p)
@ -329,16 +323,3 @@ class RoomCollection(AbstractDataCollection):
@property
def spawn_rooms(self):
return [room for room in self.values() if room.spawn]
async def setup_tables(self):
async with db.transaction():
async for table in RoomTable.query.gino.iterate():
self[table.room_id].tables[table.id] = table
table.room = self[table.room_id]
table.logic = RoomTable.GameClassMapping[table.game]()
async def setup_waddles(self):
async with db.transaction():
async for waddle in RoomWaddle.query.gino.iterate():
self[waddle.room_id].waddles[waddle.id] = waddle
waddle.room = self[waddle.room_id]

View File

@ -1,4 +1,4 @@
from houdini.data import db, AbstractDataCollection
from houdini.data import AbstractDataCollection, db
class Stamp(db.Model):

View File

@ -1,49 +0,0 @@
from abc import ABC
from abc import abstractmethod
class ITable(ABC):
"""
All table game logic classes must implement this interface.
"""
@abstractmethod
def make_move(self, *args):
"""Tells logic a move has been made."""
@abstractmethod
def is_valid_move(self, *args):
"""Returns true if the move is valid."""
@abstractmethod
def get_string(self):
"""Returns string representation of the game."""
class IWaddle(ABC):
"""
All waddle game logic classes must implement this interface.
"""
@property
@abstractmethod
def __room_id__(self):
"""External ID of waddle game room."""
def __init__(self, waddle):
self.penguins = list(waddle.penguins)
self.seats = waddle.seats
async def start(self):
room_id = type(self).__room_id__
for penguin in self.penguins:
penguin.waddle = self
await penguin.join_room(penguin.server.rooms[room_id])
async def remove_penguin(self, p):
self.penguins.remove(p)
p.waddle = None
async def send_xt(self, *data):
for penguin in self.penguins:
await penguin.send_xt(*data)

View File

@ -1,45 +0,0 @@
from houdini.games import ITable
class ConnectFourLogic(ITable):
def __init__(self):
self.current_player = 1
self.board = [[0 for _ in range(6)] for _ in range(7)]
def make_move(self, col, row):
self.board[col][row] = self.current_player
def is_valid_move(self, col, row):
if 0 <= row <= 5 and 0 <= col <= 6:
if row == 5 or (self.board[col][row] == 0 and self.board[col][row + 1]):
return True
return False
def get_string(self):
return ','.join(str(item) for row in self.board for item in row)
def is_position_win(self, col, row):
for delta_row, delta_col in [(1, 0), (0, 1), (1, 1), (1, -1)]:
streak = 1
for delta in (1, -1):
delta_row *= delta
delta_col *= delta
next_row = row + delta_row
next_col = col + delta_col
while 0 <= next_row < 6 and 0 <= next_col < 7:
if self.board[next_col][next_row] == self.current_player:
streak += 1
else:
break
if streak == 4:
return True
next_row += delta_row
next_col += delta_col
return False
def is_board_full(self):
for col in self.board:
if not col[0]:
return False
return True

View File

@ -1,63 +0,0 @@
from houdini.games import ITable
class MancalaLogic(ITable):
def __init__(self):
self.current_player = 1
self.board = [
4, 4, 4, 4, 4, 4, 0,
4, 4, 4, 4, 4, 4, 0
]
def make_move(self, hollow):
capture = False
hand = self.board[hollow]
self.board[hollow] = 0
while hand > 0:
hollow = (hollow + 1) % len(self.board)
my_mancala, opponent_mancala = (6, 13) if self.current_player == 1 else (13, 6)
if hollow == opponent_mancala:
continue
opposite_hollow = 12 - hollow
if hand == 1 and self.board[hollow] == 0:
if (self.current_player == 1 and hollow in range(0, 6)) or (self.current_player == 2 and hollow in range(7, 13)):
self.board[my_mancala] += self.board[opposite_hollow] + 1
self.board[opposite_hollow] = 0
capture = True
break
self.board[hollow] += 1
hand -= 1
if (self.current_player == 1 and hollow != 6) or (self.current_player == 2 and hollow != 13):
return 'c' if capture else str()
else:
self.current_player = 2 if self.current_player == 1 else 1
return 'f'
def is_valid_move(self, hollow):
if self.current_player == 1 and hollow not in range(0, 6):
return False
elif self.current_player == 2 and hollow not in range(7, 13):
return False
return True
def get_string(self):
return ','.join(map(str, self.board))
def is_position_win(self):
if sum(self.board[0:6]) == 0 or sum(self.board[7:-1]) == 0:
if sum(self.board[0:6]) > sum(self.board[7:-1]):
return self.current_player == 1
return self.current_player == 2
return False
def is_position_tie(self):
if sum(self.board[0:6]) == 0 or sum(self.board[7:-1]) == 0:
if sum(self.board[0:6]) == sum(self.board[7:-1]):
return True
return False

View File

@ -1,19 +0,0 @@
from houdini.games import IWaddle
class SledRacingLogic(IWaddle):
__room_id__ = 999
def __init__(self, waddle):
super().__init__(waddle)
self.payouts = [20, 10, 5, 5]
async def remove_penguin(self, p):
await super().remove_penguin(p)
await self.send_xt('uz', self.seats, *(f'{penguin.safe_name}|{penguin.color}|'
f'{penguin.hand}|{penguin.safe_name}' for penguin in self.penguins))
def get_payout(self):
return self.payouts.pop(0)

View File

@ -1,129 +0,0 @@
from houdini.games import ITable
import random
class TreasureHuntLogic(ITable):
def __init__(self):
self.map_width = 10
self.map_height = 10
self.coins_hidden = 0
self.gems_hidden = 0
self.turns = 12
self.gem_value = 25
self.coin_value = 1
self.gem_locations = []
self.treasure_map = []
self.coins_found = 0
self.gems_found = 0
self.emerald_found = 0
self.dig_record_names = []
self.dig_record_directions = []
self.dig_record_numbers = []
self.emerald = 0
self.current_player = 1
self.generate_map()
def make_move(self, movie, direction, spade):
if direction == 'right':
row = self.treasure_map[spade]
for column, tiles in enumerate(row):
self.dig(spade, column)
elif direction == 'down':
for row, columns in enumerate(self.treasure_map):
self.dig(row, spade)
self.turns -= 1
self.dig_record_names.append(movie)
self.dig_record_directions.append(direction)
self.dig_record_numbers.append(spade)
def is_valid_move(self, movie, direction, spade):
test_movie = direction + 'button' + str(spade) + '_mc'
if test_movie == movie and direction in ['down', 'right'] and 0 <= spade <= 9:
if direction == 'right':
row = self.treasure_map[spade]
for column, tiles in enumerate(row):
treasure, digs = self.treasure_map[spade][column]
if digs == 2:
return False
elif direction == 'down':
for row, columns in enumerate(self.treasure_map):
treasure, digs = self.treasure_map[row][spade]
if digs == 2:
return False
return True
return False
def get_string(self):
treasure_map = ','.join(str(item) for row in self.treasure_map for item, digs in row)
gem_locations = ','.join(self.gem_locations)
game_array = [self.map_width, self.map_height, self.coins_hidden, self.gems_hidden, self.turns,
self.gem_value, self.coin_value, gem_locations, treasure_map]
if self.dig_record_numbers:
game_array += [self.coins_found, self.gems_found, self.emerald_found]
game_array += [','.join(self.dig_record_names), ','.join(self.dig_record_directions),
','.join(map(str, self.dig_record_numbers))]
return '%'.join(map(str, game_array))
def generate_map(self):
for row in range(self.map_height):
self.treasure_map.append([])
for column in range(self.map_width):
self.treasure_map[row].append([self.generate_treasure(row, column), 0])
def generate_treasure(self, row, column):
treasure_type = [('None', 0), ('Coin', 1), ('Gem', 2), ('Emerald', 4)]
if self.get_gem_by_piece(row, column):
return 3
if row + 1 == self.map_height or column + 1 == self.map_width:
treasure_type = treasure_type[:2]
name, value = random.choices(treasure_type, weights=[60, 40, 1, 0.5][:len(treasure_type)])[0]
self.coins_hidden += 1 if value == 1 else self.coins_hidden
if value > 1:
self.gems_hidden += 1
self.gem_locations.append(str(row) + ',' + str(column))
if self.emerald:
return 2
if value == 4 and not self.emerald:
self.emerald = 1
return value
def get_gem_by_piece(self, row, column):
for delta_row, delta_col in [(0, -1), (-1, -1), (-1, 0)]:
if row > 0 and column > 0:
treasure, digs = self.treasure_map[row + delta_row][column + delta_col]
if treasure == 2 or treasure == 4:
return row + delta_row, column + delta_col
return False
def is_gem_uncovered(self, row, column):
for delta_row, delta_col in [(0, 1), (1, 1), (1, 0)]:
treasure, digs = self.treasure_map[row + delta_row][column + delta_col]
if digs != 2:
return False
return True
def dig(self, row, column):
self.treasure_map[row][column][1] += 1
treasure, digs = self.treasure_map[row][column]
if digs == 2:
if treasure == 1:
self.coins_found += 1
elif treasure == 2 or treasure == 4:
if not self.is_gem_uncovered(row, column):
return
self.gems_found += 1
elif treasure == 3:
treasure_row, treasure_col = self.get_gem_by_piece(row, column)
if not self.is_gem_uncovered(treasure_row, treasure_col):
return
self.gems_found += 1
if treasure == 4:
self.emerald_found = 1
def determine_winnings(self):
total = self.coins_found * self.coin_value
total += self.gems_found * self.gem_value
total += self.emerald_found * self.gem_value * 3
return total

View File

@ -1,12 +1,12 @@
import inspect
import enum
import inspect
import itertools
from types import FunctionType
from houdini.converters import _listener, _ArgumentDeserializer, get_converter, \
do_conversion, _ConverterContext, ChecklistError
from houdini.cooldown import _Cooldown, _CooldownMapping, BucketType, CooldownError
from houdini import plugins, _AbstractManager, get_package_modules
from houdini import _AbstractManager, get_package_modules, plugins
from houdini.converters import ChecklistError, _ArgumentDeserializer, _ConverterContext, _listener, do_conversion, \
get_converter
from houdini.cooldown import BucketType, CooldownError, _Cooldown, _CooldownMapping
class AuthorityError(Exception):
@ -258,3 +258,19 @@ def player_in_room(*room_ids):
def check_room_id(_, p):
return p.room is not None and p.room.id in room_ids
return check(check_room_id)
def table(*logic):
def check_table_game(_, p):
if p.table is not None and type(p.table.logic) in logic:
return True
return False
return check(check_table_game)
def waddle(*waddle):
def check_waddle_game(_, p):
if p.waddle is not None and type(p.waddle) in waddle:
return True
return False
return check(check_waddle_game)

View File

@ -1,17 +1,16 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.converters import OptionalConverter
from houdini.constants import ClientType
from houdini.handlers.play.navigation import handle_join_room
from houdini.handlers.play.moderation import cheat_ban
from houdini.data.room import Room
from houdini.data.game import PenguinGameData
import random
import time
from sqlalchemy.dialects.postgresql import insert
import time
import random
from houdini import handlers
from houdini.constants import ClientType
from houdini.converters import OptionalConverter
from houdini.data.game import PenguinGameData
from houdini.data.room import Room
from houdini.handlers import XTPacket
from houdini.handlers.play.moderation import cheat_ban
from houdini.handlers.play.navigation import handle_join_room
default_score_games = {904, 905, 906, 912, 916, 917, 918, 919, 950, 952}

View File

@ -1,15 +1,14 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.data.dance import DanceSongCollection
from houdini.penguin import Penguin
import random
import time
import asyncio
import itertools
import random
import time
from dataclasses import dataclass
from houdini import handlers
from houdini.data.dance import DanceSongCollection
from houdini.handlers import XTPacket
from houdini.penguin import Penguin
@dataclass
class Dancer:
@ -56,6 +55,11 @@ class DanceFloor:
self._dancers.pop(p.id)
await self.send_xt('zm', self.get_string())
async def start(self):
while True:
await self.next_round()
await asyncio.sleep(self._current_track.song_length_millis // 1000)
async def next_round(self):
self._current_track = self._queued_track
@ -84,9 +88,6 @@ class DanceFloor:
self._queued_track = next(self._tracks)
self._next_song_timestamp = int(round(time.time() * 1000)) + self._current_track.song_length_millis
await asyncio.sleep(self._current_track.song_length_millis // 1000)
await self.next_round()
async def send_xt(self, *data):
for dancer in self._dancers.values():
await dancer.penguin.send_xt(*data)
@ -147,7 +148,7 @@ async def songs_load(server):
server.logger.info(f'Loaded {len(server.dance_songs)} dance tracks')
server.dance_floor = DanceFloor(server)
asyncio.create_task(server.dance_floor.next_round())
asyncio.create_task(server.dance_floor.start())
@handlers.handler(XTPacket('gz', ext='z'))

View File

@ -1,17 +1,59 @@
from houdini import handlers
from houdini import ITable, handlers
from houdini.handlers import XTPacket
from houdini.handlers.games.table import table_handler
from houdini.games.four import ConnectFourLogic
class ConnectFourLogic(ITable):
def __init__(self):
self.current_player = 1
self.board = [[0 for _ in range(6)] for _ in range(7)]
def make_move(self, col, row):
self.board[col][row] = self.current_player
def is_valid_move(self, col, row):
if 0 <= row <= 5 and 0 <= col <= 6:
if row == 5 or (self.board[col][row] == 0 and self.board[col][row + 1]):
return True
return False
def get_string(self):
return ','.join(str(item) for row in self.board for item in row)
def is_position_win(self, col, row):
for delta_row, delta_col in [(1, 0), (0, 1), (1, 1), (1, -1)]:
streak = 1
for delta in (1, -1):
delta_row *= delta
delta_col *= delta
next_row = row + delta_row
next_col = col + delta_col
while 0 <= next_row < 6 and 0 <= next_col < 7:
if self.board[next_col][next_row] == self.current_player:
streak += 1
else:
break
if streak == 4:
return True
next_row += delta_row
next_col += delta_col
return False
def is_board_full(self):
for col in self.board:
if not col[0]:
return False
return True
@handlers.handler(XTPacket('gz', ext='z'))
@table_handler(ConnectFourLogic)
@handlers.table(ConnectFourLogic)
async def handle_get_game(p):
await p.send_xt('gz', p.table.get_string())
@handlers.handler(XTPacket('jz', ext='z'))
@table_handler(ConnectFourLogic)
@handlers.table(ConnectFourLogic)
async def handle_join_game(p):
game_full = len(p.table.penguins) > 2
if not game_full:
@ -24,7 +66,7 @@ async def handle_join_game(p):
@handlers.handler(XTPacket('zm', ext='z'))
@table_handler(ConnectFourLogic)
@handlers.table(ConnectFourLogic)
async def handle_send_move(p, col: int, row: int):
try:
seat_id = p.table.get_seat_id(p)

View File

@ -1,17 +1,77 @@
from houdini import handlers
from houdini import ITable, handlers
from houdini.handlers import XTPacket
from houdini.handlers.games.table import table_handler
from houdini.games.mancala import MancalaLogic
class MancalaLogic(ITable):
def __init__(self):
self.current_player = 1
self.board = [
4, 4, 4, 4, 4, 4, 0,
4, 4, 4, 4, 4, 4, 0
]
def make_move(self, hollow):
capture = False
hand = self.board[hollow]
self.board[hollow] = 0
while hand > 0:
hollow = (hollow + 1) % len(self.board)
my_mancala, opponent_mancala = (6, 13) if self.current_player == 1 else (13, 6)
if hollow == opponent_mancala:
continue
opposite_hollow = 12 - hollow
if hand == 1 and self.board[hollow] == 0:
if (self.current_player == 1 and hollow in range(0, 6)) or (self.current_player == 2 and hollow in range(7, 13)):
self.board[my_mancala] += self.board[opposite_hollow] + 1
self.board[opposite_hollow] = 0
capture = True
break
self.board[hollow] += 1
hand -= 1
if (self.current_player == 1 and hollow != 6) or (self.current_player == 2 and hollow != 13):
return 'c' if capture else str()
else:
self.current_player = 2 if self.current_player == 1 else 1
return 'f'
def is_valid_move(self, hollow):
if self.current_player == 1 and hollow not in range(0, 6):
return False
elif self.current_player == 2 and hollow not in range(7, 13):
return False
return True
def get_string(self):
return ','.join(map(str, self.board))
def is_position_win(self):
if sum(self.board[0:6]) == 0 or sum(self.board[7:-1]) == 0:
if sum(self.board[0:6]) > sum(self.board[7:-1]):
return self.current_player == 1
return self.current_player == 2
return False
def is_position_tie(self):
if sum(self.board[0:6]) == 0 or sum(self.board[7:-1]) == 0:
if sum(self.board[0:6]) == sum(self.board[7:-1]):
return True
return False
@handlers.handler(XTPacket('gz', ext='z'))
@table_handler(MancalaLogic)
@handlers.table(MancalaLogic)
async def handle_get_game(p):
await p.send_xt('gz', p.table.get_string())
@handlers.handler(XTPacket('jz', ext='z'))
@table_handler(MancalaLogic)
@handlers.table(MancalaLogic)
async def handle_join_game(p):
game_full = len(p.table.penguins) > 2
if not game_full:
@ -24,7 +84,7 @@ async def handle_join_game(p):
@handlers.handler(XTPacket('zm', ext='z'))
@table_handler(MancalaLogic)
@handlers.table(MancalaLogic)
async def handle_send_move(p, hollow: int):
try:
seat_id = p.table.get_seat_id(p)

View File

@ -0,0 +1,187 @@
import asyncio
import operator
from dataclasses import dataclass
from houdini import handlers
from houdini.data.room import RoomWaddle
from houdini.handlers import XTPacket
from houdini.handlers.games.ninja.card import CardJitsuLogic
from houdini.handlers.games.ninja.fire import CardJitsuFireLogic
from houdini.handlers.games.ninja.water import CardJitsuWaterLogic
from houdini.penguin import Penguin
@dataclass
class MatchMaker:
penguin: Penguin
tick: int
class MatchMaking:
SenseiRoom = 951
SenseiFireRoom = 953
SenseiWaterRoom = 954
def __init__(self, server, on_tick, on_matched, match_by, max_players=2, match_every=10):
self.server = server
self._on_tick = on_tick
self._on_matched = on_matched
self._match_by = match_by
self._max_players = max_players
self._match_every = match_every
self._penguins = []
self._matched_penguins = {}
async def start(self):
while True:
await self.tick()
await asyncio.sleep(1)
async def tick(self):
for i in range(0, len(self._penguins) - len(self._penguins) % 2, self._max_players):
matched = self._penguins[i:i+self._max_players]
if any(mm.tick == 0 for mm in matched):
matched_penguins = [mm.penguin for mm in matched]
self._matched_penguins.update({
mm.penguin.id: matched_penguins
for mm in matched})
self._penguins = [mm for mm in self._penguins if mm not in matched]
await self._on_matched(matched)
else:
await self._on_tick(matched)
for mm in matched:
mm.tick -= 1
def add_penguin(self, p):
mm = MatchMaker(penguin=p, tick=self._match_every)
self._penguins.append(mm)
self._penguins.sort(key=operator.attrgetter('penguin.' + self._match_by))
def remove_penguin(self, p):
self._penguins = [mm for mm in self._penguins if mm.penguin != p]
if p.id in self._matched_penguins:
matched = self._matched_penguins[p.id]
matched.remove(p)
del self._matched_penguins[p.id]
self._penguins.sort(key=operator.attrgetter('penguin.' + self._match_by))
def matched_with(self, p):
if p.id in self._matched_penguins:
return self._matched_penguins[p.id]
def has_matched(self, p):
return p.id in self._matched_penguins
async def card_tick(matched):
nicknames = [mm.penguin.safe_name for mm in matched]
for mm in matched:
await mm.penguin.send_xt('tmm', mm.tick, *nicknames)
async def card_color_tick(matched):
nicknames = [f'{mm.penguin.safe_name}|{mm.penguin.color}' for mm in matched]
for mm in matched:
await mm.penguin.send_xt('tmm', len(matched), mm.tick, *nicknames)
def card_match(waddle_room_id, waddle_game):
async def match(matched):
nicknames = [f'{mm.penguin.safe_name}|{mm.penguin.color}' for mm in matched]
host = matched[0].penguin
waddle_room = host.server.rooms[waddle_room_id]
rw = RoomWaddle(id=host.id, room_id=waddle_room.id, seats=len(matched), game=waddle_game, temporary=True)
waddle_room.waddles[rw.id] = rw
for mm in matched:
await mm.penguin.send_xt('scard', waddle_room.id, rw.id, len(matched), mm.tick, *nicknames)
return match
card_matched = card_match(CardJitsuLogic.room_id, 'card')
card_fire_matched = card_match(CardJitsuFireLogic.room_id, 'fire')
card_water_matched = card_match(CardJitsuWaterLogic.room_id, 'water')
@handlers.boot
async def match_load(server):
server.match_making = MatchMaking(server, card_tick, card_matched, match_by='ninja_rank')
server.fire_match_making = MatchMaking(server, card_color_tick, card_fire_matched,
match_by='fire_ninja_rank', max_players=4)
server.water_match_making = MatchMaking(server, card_color_tick, card_water_matched,
match_by='water_ninja_rank', max_players=4)
asyncio.create_task(server.match_making.start())
asyncio.create_task(server.fire_match_making.start())
asyncio.create_task(server.water_match_making.start())
@handlers.handler(XTPacket('jmm', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiRoom)
async def handle_join_match_making(p):
p.server.match_making.add_penguin(p)
await p.send_xt('jmm', p.safe_name)
@handlers.handler(XTPacket('jmm', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiFireRoom)
async def handle_join_fire_match_making(p):
p.server.fire_match_making.add_penguin(p)
await p.send_xt('jmm', p.safe_name)
@handlers.handler(XTPacket('jmm', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiWaterRoom)
async def handle_join_water_match_making(p):
p.server.water_match_making.add_penguin(p)
await p.send_xt('jmm', p.safe_name)
@handlers.handler(XTPacket('jsen', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiRoom)
async def handle_join_sensei_match(p):
waddle_room = p.server.rooms[CardJitsuLogic.__room_id__]
rw = RoomWaddle(id=p.id, room_id=waddle_room.id, seats=1, game='sensei', temporary=True)
waddle_room.waddles[rw.id] = rw
await p.send_xt('scard', waddle_room.id, rw.id, 1, 0, f'{p.safe_name}|{p.color}')
@handlers.handler(XTPacket('jsen', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiFireRoom)
async def handle_join_fire_sensei_match(p):
waddle_room = p.server.rooms[CardJitsuFireLogic.__room_id__]
rw = RoomWaddle(id=p.id, room_id=waddle_room.id, seats=1, game='firesensei', temporary=True)
waddle_room.waddles[rw.id] = rw
await p.send_xt('scard', waddle_room.id, rw.id, 1, 0, f'{p.safe_name}|{p.color}')
@handlers.handler(XTPacket('jsen', ext='z'))
@handlers.player_in_room(MatchMaking.SenseiWaterRoom)
async def handle_join_water_sensei_match(p):
waddle_room = p.server.rooms[CardJitsuWaterLogic.__room_id__]
rw = RoomWaddle(id=p.id, room_id=waddle_room.id, seats=1, game='watersensei', temporary=True)
waddle_room.waddles[rw.id] = rw
await p.send_xt('scard', waddle_room.id, rw.id, 1, 0, f'{p.safe_name}|{p.color}')
@handlers.handler(XTPacket('lmm', ext='z'))
async def handle_leave_match_making(p):
p.server.match_making.remove_penguin(p)
p.server.water_match_making.remove_penguin(p)
p.server.fire_match_making.remove_penguin(p)
@handlers.disconnected
@handlers.player_attribute(joined_world=True)
async def handle_disconnect_match_making(p):
p.server.match_making.remove_penguin(p)
p.server.water_match_making.remove_penguin(p)
p.server.fire_match_making.remove_penguin(p)

View File

@ -0,0 +1,201 @@
import itertools
from dataclasses import dataclass
from typing import Dict, List, Union
from houdini import IWaddle
from houdini.data.ninja import Card
from houdini.penguin import Penguin
@dataclass
class Played:
id: int
card: Card
player: int
opponent: int
@dataclass
class Ninja:
penguin: Penguin
deck: Dict[int, Card]
bank: Dict[str, List[Played]]
chosen: Union[Played, None]
class CardJitsuLogic(IWaddle):
room_id = 998
rule_set = {'f': 's', 'w': 'f', 's': 'w'}
discard_elements = {4: 's', 5: 'w', 6: 'f'}
discard_colors = {7: 'r', 8: 'b', 9: 'g', 10: 'y', 11: 'o', 12: 'p'}
replacements = {16: ['w', 'f'], 17: ['s', 'w'], 18: ['f', 's']}
rank_speed = 1
def __init__(self, waddle):
super().__init__(waddle)
self.ninjas = [
Ninja(
penguin=p,
deck={},
bank={'f': [], 'w': [], 's': []},
chosen=None
) for p in waddle.penguins]
self.card_id = 1
self.powers = {}
self.discards = []
def get_winning_cards(self, seat_id):
player_cards = self.ninjas[seat_id].bank
for element, cards in player_cards.items():
color_cards, colors = [], []
for card in cards:
if card.card.color not in colors:
color_cards.append(card)
colors.append(card.card.color)
if len(color_cards) == 3:
return color_cards, 0
elements = player_cards.values()
for combo in itertools.product(*elements):
colors = {card.card.color for card in combo}
if len(colors) == 3:
return combo, 1
return False, -1
def has_cards_to_play(self, seat_id):
power_limiters = {13: 's', 14: 'f', 15: 'w'}
for power_id, element in power_limiters.items():
if power_id in self.powers:
power_card = self.powers[power_id]
if power_card.opponent == seat_id:
opponent_deck = self.ninjas[power_card.opponent].deck
for card_id, card in opponent_deck.items():
if card.element != element:
return True
return False
return True
def discard_opponent_card(self, power_id, opponent_seat_id):
opponent_cards = self.ninjas[opponent_seat_id].bank
if power_id in self.discard_elements:
element_to_discard = self.discard_elements[power_id]
if len(opponent_cards[element_to_discard]) > 0:
card_to_discard = self.ninjas[opponent_seat_id].bank[element_to_discard][-1]
self.discards.append(card_to_discard.id)
del self.ninjas[opponent_seat_id].bank[element_to_discard][-1]
return True
if power_id in self.discard_colors:
color_to_discard = self.discard_colors[power_id]
for element, cards in opponent_cards.items():
for index, card in enumerate(cards):
if card.card.color == color_to_discard:
card_to_discard = self.ninjas[opponent_seat_id].bank[element][index]
self.discards.append(card_to_discard.id)
del self.ninjas[opponent_seat_id].bank[element][index]
return True
return False
def adjust_card_values(self, first_card, second_card):
for power_id, power_card in self.powers.items():
if power_card.card.power_id == 1 and first_card.card.element == second_card.card.element:
first_card.card.value = 1
second_card.card.value = 1
if power_card.card.power_id == 2:
if power_card.player == 0:
first_card.card.value += 2
else:
second_card.card.value += 2
if power_card.card.power_id == 3:
if power_card.player == 0:
second_card.card.value -= 2
else:
first_card.card.value -= 2
def get_round_winner(self):
first_card, second_card = self.ninjas[0].chosen, self.ninjas[1].chosen
winner_seat_id = self.get_winner_seat_id(first_card, second_card)
self.adjust_card_values(first_card, second_card)
for ninja_seat_id, ninja in enumerate(self.ninjas):
played_card = ninja.chosen
power_id = played_card.card.power_id
if not power_id:
continue
opponent_seat_id = 1 if ninja_seat_id == 0 else 0
on_played = power_id in {1, 16, 17, 18}
on_scored = not on_played
current_round = power_id in {4, 5, 6, 7, 8, 9, 10, 11, 12, 16, 17, 18}
next_round = not current_round
if on_played and next_round:
self.powers[power_id] = played_card
if on_scored and ninja_seat_id == winner_seat_id:
if next_round:
self.powers[power_id] = played_card
if current_round:
self.discard_opponent_card(power_id, opponent_seat_id)
if on_played and current_round:
self.replace_opponent_card(power_id, first_card, second_card, played_card.player)
winner_seat_id = self.get_winner_seat_id(first_card, second_card)
self.ninjas[0].chosen = None
self.ninjas[1].chosen = None
return winner_seat_id
@classmethod
def replace_opponent_card(cls, power_id, first_card, second_card, seat_id):
for replace_power_id, replacement in cls.replacements.items():
if power_id == replace_power_id:
original, replace = replacement
if seat_id == 1 and first_card.card.element == original:
first_card.card.element = replace
if seat_id == 0 and second_card.card.element == original:
second_card.card.element = replace
@classmethod
def get_winner_seat_id(cls, first_card, second_card):
if first_card.card.element != second_card.card.element:
return 0 if cls.rule_set[first_card.card.element] == second_card.card.element else 1
elif first_card.card.value > second_card.card.value:
return 0
elif second_card.card.value > first_card.card.value:
return 1
return -1
class CardJitsuMatLogic(CardJitsuLogic):
rank_speed = 0.5
class SenseiLogic(CardJitsuLogic):
def __init__(self, waddle):
super().__init__(waddle)
self.ninja = Ninja(
penguin=waddle.penguins[0],
deck={},
bank={'f': [], 'w': [], 's': []},
chosen=None
)
self.sensei_move = {}
self.colors = []
def get_win_card(self, card):
self.colors = [] if len(self.colors) >= 6 else self.colors
for card_check in self.ninja.penguin.server.cards.values():
if self.beats_card(card_check, card) and card_check.color not in self.colors:
self.colors.append(card_check.color)
return card_check
@classmethod
def beats_card(cls, card_check, card_play):
if card_check.card.element != card_play.card.element:
return True if cls.rule_set[card_check.card.element] == card_play.card.element else False
elif card_check.card.value > card_play.card.value:
return True
return False

View File

@ -0,0 +1,15 @@
from houdini import IWaddle
class CardJitsuFireLogic(IWaddle):
room_id = 997
def __init__(self, waddle):
super().__init__(waddle)
class FireSenseiLogic(CardJitsuFireLogic):
def __init__(self, waddle):
super().__init__(waddle)

View File

@ -0,0 +1,15 @@
from houdini import IWaddle
class CardJitsuWaterLogic(IWaddle):
room_id = 995
def __init__(self, waddle):
super().__init__(waddle)
class WaterSenseiLogic(CardJitsuWaterLogic):
def __init__(self, waddle):
super().__init__(waddle)

View File

@ -1,24 +1,40 @@
from houdini import handlers
from houdini import IWaddle, handlers
from houdini.handlers import XTPacket
from houdini.handlers.games.waddle import waddle_handler
from houdini.games.sled import SledRacingLogic
class SledRacingLogic(IWaddle):
room_id = 999
def __init__(self, waddle):
super().__init__(waddle)
self.payouts = [20, 10, 5, 5]
async def remove_penguin(self, p):
await super().remove_penguin(p)
await self.send_xt('uz', self.seats, *(f'{penguin.safe_name}|{penguin.color}|'
f'{penguin.hand}|{penguin.safe_name}' for penguin in self.penguins))
def get_payout(self):
return self.payouts.pop(0)
@handlers.handler(XTPacket('jz', ext='z'))
@waddle_handler(SledRacingLogic)
@handlers.waddle(SledRacingLogic)
async def handle_join_game(p):
await p.send_xt('uz', p.waddle.seats, *(f'{penguin.safe_name}|{penguin.color}|'
f'{penguin.hand or 0}|{penguin.nickname}' for penguin in p.waddle.penguins))
@handlers.handler(XTPacket('zm', ext='z'))
@waddle_handler(SledRacingLogic)
@handlers.waddle(SledRacingLogic)
async def handle_send_move(p, player_id: int, x: float, y: float, time: float):
await p.waddle.send_xt('zm', player_id, x, y, time)
@handlers.handler(XTPacket('zo', ext='z'))
@waddle_handler(SledRacingLogic)
@handlers.waddle(SledRacingLogic)
async def handle_game_over(p):
coins = p.waddle.get_payout()
await p.add_coins(coins)

View File

@ -1,14 +1,6 @@
from houdini import handlers
from houdini.handlers import XTPacket, check
from houdini.handlers.play.navigation import handle_join_room, handle_join_player_room
def table_handler(logic):
def check_table_game(_, p):
if p.table is not None and type(p.table.logic) == logic:
return True
return False
return check(check_table_game)
from houdini.handlers import XTPacket
from houdini.handlers.play.navigation import handle_join_player_room, handle_join_room
@handlers.handler(XTPacket('a', 'gt'))

View File

@ -1,11 +1,137 @@
from houdini import handlers
import random
from houdini import ITable, handlers
from houdini.handlers import XTPacket
from houdini.handlers.games.table import table_handler
from houdini.games.treasure import TreasureHuntLogic
class TreasureHuntLogic(ITable):
def __init__(self):
self.map_width = 10
self.map_height = 10
self.coins_hidden = 0
self.gems_hidden = 0
self.turns = 12
self.gem_value = 25
self.coin_value = 1
self.gem_locations = []
self.treasure_map = []
self.coins_found = 0
self.gems_found = 0
self.emerald_found = 0
self.dig_record_names = []
self.dig_record_directions = []
self.dig_record_numbers = []
self.emerald = 0
self.current_player = 1
self.generate_map()
def make_move(self, movie, direction, spade):
if direction == 'right':
row = self.treasure_map[spade]
for column, tiles in enumerate(row):
self.dig(spade, column)
elif direction == 'down':
for row, columns in enumerate(self.treasure_map):
self.dig(row, spade)
self.turns -= 1
self.dig_record_names.append(movie)
self.dig_record_directions.append(direction)
self.dig_record_numbers.append(spade)
def is_valid_move(self, movie, direction, spade):
test_movie = direction + 'button' + str(spade) + '_mc'
if test_movie == movie and direction in ['down', 'right'] and 0 <= spade <= 9:
if direction == 'right':
row = self.treasure_map[spade]
for column, tiles in enumerate(row):
treasure, digs = self.treasure_map[spade][column]
if digs == 2:
return False
elif direction == 'down':
for row, columns in enumerate(self.treasure_map):
treasure, digs = self.treasure_map[row][spade]
if digs == 2:
return False
return True
return False
def get_string(self):
treasure_map = ','.join(str(item) for row in self.treasure_map for item, digs in row)
gem_locations = ','.join(self.gem_locations)
game_array = [self.map_width, self.map_height, self.coins_hidden, self.gems_hidden, self.turns,
self.gem_value, self.coin_value, gem_locations, treasure_map]
if self.dig_record_numbers:
game_array += [self.coins_found, self.gems_found, self.emerald_found]
game_array += [','.join(self.dig_record_names), ','.join(self.dig_record_directions),
','.join(map(str, self.dig_record_numbers))]
return '%'.join(map(str, game_array))
def generate_map(self):
for row in range(self.map_height):
self.treasure_map.append([])
for column in range(self.map_width):
self.treasure_map[row].append([self.generate_treasure(row, column), 0])
def generate_treasure(self, row, column):
treasure_type = [('None', 0), ('Coin', 1), ('Gem', 2), ('Emerald', 4)]
if self.get_gem_by_piece(row, column):
return 3
if row + 1 == self.map_height or column + 1 == self.map_width:
treasure_type = treasure_type[:2]
name, value = random.choices(treasure_type, weights=[60, 40, 1, 0.5][:len(treasure_type)])[0]
self.coins_hidden += 1 if value == 1 else self.coins_hidden
if value > 1:
self.gems_hidden += 1
self.gem_locations.append(str(row) + ',' + str(column))
if self.emerald:
return 2
if value == 4 and not self.emerald:
self.emerald = 1
return value
def get_gem_by_piece(self, row, column):
for delta_row, delta_col in [(0, -1), (-1, -1), (-1, 0)]:
if row > 0 and column > 0:
treasure, digs = self.treasure_map[row + delta_row][column + delta_col]
if treasure == 2 or treasure == 4:
return row + delta_row, column + delta_col
return False
def is_gem_uncovered(self, row, column):
for delta_row, delta_col in [(0, 1), (1, 1), (1, 0)]:
treasure, digs = self.treasure_map[row + delta_row][column + delta_col]
if digs != 2:
return False
return True
def dig(self, row, column):
self.treasure_map[row][column][1] += 1
treasure, digs = self.treasure_map[row][column]
if digs == 2:
if treasure == 1:
self.coins_found += 1
elif treasure == 2 or treasure == 4:
if not self.is_gem_uncovered(row, column):
return
self.gems_found += 1
elif treasure == 3:
treasure_row, treasure_col = self.get_gem_by_piece(row, column)
if not self.is_gem_uncovered(treasure_row, treasure_col):
return
self.gems_found += 1
if treasure == 4:
self.emerald_found = 1
def determine_winnings(self):
total = self.coins_found * self.coin_value
total += self.gems_found * self.gem_value
total += self.emerald_found * self.gem_value * 3
return total
@handlers.handler(XTPacket('gz', ext='z'))
@table_handler(TreasureHuntLogic)
@handlers.table(TreasureHuntLogic)
async def handle_get_game(p):
if len(p.table.penguins) == 2:
player_one = p.table.penguins[0]
@ -15,7 +141,7 @@ async def handle_get_game(p):
@handlers.handler(XTPacket('jz', ext='z'))
@table_handler(TreasureHuntLogic)
@handlers.table(TreasureHuntLogic)
async def handle_join_game(p):
game_full = len(p.table.penguins) > 2
if not game_full:
@ -27,7 +153,7 @@ async def handle_join_game(p):
@handlers.handler(XTPacket('zm', ext='z'))
@table_handler(TreasureHuntLogic)
@handlers.table(TreasureHuntLogic)
async def handle_send_move(p, movie: str, direction: str, spade: int):
try:
seat_id = p.table.get_seat_id(p)

View File

@ -1,14 +1,7 @@
from houdini import handlers
from houdini.handlers import XTPacket, check
from houdini.handlers.play.navigation import handle_join_room, handle_join_player_room
def waddle_handler(waddle):
def check_waddle_game(_, p):
if p.waddle is not None and type(p.waddle) == waddle:
return True
return False
return check(check_waddle_game)
from houdini.data.room import Room
from houdini.handlers import XTPacket
from houdini.handlers.play.navigation import handle_join_player_room, handle_join_room
@handlers.handler(XTPacket('gw', ext='z'))
@ -32,6 +25,11 @@ async def handle_leave_waddle(p):
await p.waddle.remove_penguin(p)
@handlers.handler(XTPacket('w', 'jx'))
async def handle_start_waddle(p, room: Room, waddle: int):
await room.waddles[waddle].add_penguin(p)
@handlers.handler(XTPacket('j', 'jr'), after=handle_join_room)
async def handle_join_room_waddle(p):
if p.waddle:

View File

@ -1,9 +1,8 @@
from houdini import handlers
from houdini.handlers import XMLPacket
from houdini.converters import VersionChkConverter
from houdini.constants import ClientType
from houdini.converters import VersionChkConverter
from houdini.data.buddy import BuddyList
from houdini.handlers import XMLPacket
@handlers.handler(XMLPacket('verChk'))

View File

@ -1,18 +1,18 @@
import asyncio
import os
from datetime import datetime, timedelta
import bcrypt
from houdini import handlers
from houdini.constants import ClientType
from houdini.converters import Credentials
from houdini.crypto import Crypto
from houdini.data.moderator import Ban
from houdini.data.penguin import Penguin
from houdini.handlers import XMLPacket
from houdini.handlers.login import get_server_presence
from houdini.handlers.play.navigation import get_minutes_played_today
from houdini.converters import Credentials
from houdini.data.penguin import Penguin
from houdini.data.moderator import Ban
from houdini.crypto import Crypto
from houdini.constants import ClientType
import asyncio
import bcrypt
import os
from datetime import datetime, timedelta
@handlers.handler(XMLPacket('login'))

View File

@ -1,13 +1,13 @@
from houdini import handlers
from houdini.handlers import XMLPacket, login
from houdini.converters import WorldCredentials, Credentials
from houdini.data.penguin import Penguin
from houdini.data.moderator import Ban
from houdini.crypto import Crypto
from houdini.constants import ClientType
from datetime import datetime
from houdini import handlers
from houdini.constants import ClientType
from houdini.converters import Credentials, WorldCredentials
from houdini.crypto import Crypto
from houdini.data.moderator import Ban
from houdini.data.penguin import Penguin
from houdini.handlers import XMLPacket, login
handle_version_check = login.handle_version_check
handle_random_key = login.handle_random_key

View File

@ -1,6 +1,6 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.constants import ClientType
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('pt', 'spts'), client=ClientType.Vanilla)

View File

@ -1,12 +1,10 @@
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket
from houdini.handlers.play.navigation import handle_join_room
from houdini.handlers import Priority
from houdini.data.penguin import Penguin
from houdini.data.buddy import BuddyList, BuddyRequest, BuddyListCollection, \
BuddyRequestCollection, CharacterBuddyCollection, CharacterCollection
from houdini.constants import ClientType
from houdini.data.buddy import BuddyList, BuddyListCollection, BuddyRequest, BuddyRequestCollection, \
CharacterBuddyCollection, CharacterCollection
from houdini.data.penguin import Penguin
from houdini.handlers import Priority, XMLPacket, XTPacket
from houdini.handlers.play.navigation import handle_join_room
async def update_player_presence(p):

View File

@ -0,0 +1,52 @@
import random
from houdini import handlers
from houdini.data.ninja import CardCollection, CardStarterDeck, PenguinCardCollection
from houdini.handlers import Priority, XMLPacket, XTPacket
@handlers.boot
async def cards_load(server):
server.cards = await CardCollection.get_collection()
server.logger.info(f'Loaded {len(server.cards)} ninja cards')
starter_deck_cards = await CardStarterDeck.query.gino.all()
server.cards.set_starter_decks(starter_deck_cards)
server.logger.info(f'Loaded {len(server.cards.starter_decks)} starter decks')
@handlers.handler(XMLPacket('login'), priority=Priority.Low)
@handlers.allow_once
async def load_card_inventory(p):
p.cards = await PenguinCardCollection.get_collection(p.id)
@handlers.handler(XTPacket('i', 'ai'))
async def handle_buy_starter_deck(p, deck_id: int):
if deck_id in p.server.cards.starter_decks:
starter_deck = p.server.cards.starter_decks[deck_id]
power_cards = [card for card, qty in starter_deck if card.power_id > 0]
for card, qty in starter_deck:
if card.power_id == 0:
await p.add_card(card, quantity=qty)
power_card = random.choice(power_cards)
await p.add_card(power_card, quantity=1)
@handlers.handler(XTPacket('cd', 'gcd'))
async def handle_get_card_data(p):
await p.send_xt('gcd', '|'.join(f'{card.card_id},{card.quantity},{card.member_quantity}'
for card in p.cards.values()))
@handlers.handler(XTPacket('cd', 'bpc'))
async def handle_buy_power_cards(p):
if p.coins >= 1500:
power_cards = random.sample(p.server.cards.power_cards, 3)
for card in power_cards:
await p.add_card(card, member_quantity=1)
await p.update(coins=p.coins - 1500).apply()
await p.send_xt('bpc', ','.join([str(card.id) for card in power_cards]), p.coins)
else:
await p.send_xt('bpc', 401)

View File

@ -1,17 +1,16 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.handlers.play.mail import handle_start_mail_engine
import datetime
import random
import time
from aiocache import cached
from houdini import handlers
from houdini.constants import ClientType
from houdini.data.item import Item
from houdini.data.mail import PenguinPostcard
from houdini.data.penguin import EpfComMessage
from houdini.constants import ClientType
import datetime
import time
import random
from aiocache import cached
from houdini.handlers import XTPacket
from houdini.handlers.play.mail import handle_start_mail_engine
@cached(alias='default', key='com_messages')

View File

@ -1,14 +1,13 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.handlers.play.navigation import handle_join_server, handle_join_room, handle_join_player_room
from houdini.data import db
from houdini.data.quest import Quest, QuestAwardItem, QuestAwardFurniture, QuestAwardPuffleItem, QuestTask
from houdini.data.quest import PenguinQuestTask
import ujson
from aiocache import cached
from houdini import handlers
from houdini.data import db
from houdini.data.quest import PenguinQuestTask, Quest, QuestAwardFurniture, QuestAwardItem, QuestAwardPuffleItem, \
QuestTask
from houdini.handlers import XTPacket
from houdini.handlers.play.navigation import handle_join_player_room, handle_join_room, handle_join_server
def get_status_key(_, p):
return f'quest.status.{p.id}'

View File

@ -1,28 +1,23 @@
import itertools
import ujson
import time
from datetime import datetime, timedelta
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.converters import SeparatorConverter
from houdini.constants import ClientType, StatusField
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, \
IglooCollection, FurnitureCollection, \
FlooringCollection, LocationCollection,\
PenguinIglooCollection, PenguinFurnitureCollection, \
PenguinFlooringCollection, PenguinLocationCollection
from houdini.data.room import PenguinIglooRoomCollection
from houdini.data.game import PenguinGameData
import ujson
from aiocache import cached
from sqlalchemy.dialects.postgresql import insert
from aiocache import cached
from houdini import handlers
from houdini.constants import ClientType, StatusField
from houdini.converters import SeparatorConverter
from houdini.data import db
from houdini.data.game import PenguinGameData
from houdini.data.igloo import Flooring, FlooringCollection, Furniture, FurnitureCollection, Igloo, IglooCollection, \
IglooFurniture, IglooLike, Location, LocationCollection, PenguinFlooringCollection, PenguinFurnitureCollection, \
PenguinIglooCollection, PenguinLocationCollection
from houdini.data.penguin import Penguin
from houdini.data.room import PenguinIglooRoom, PenguinIglooRoomCollection
from houdini.handlers import Priority, XMLPacket, XTPacket
from houdini.handlers.play.navigation import handle_join_server
def get_layout_furniture_key(_, p, igloo_id):

View File

@ -1,8 +1,7 @@
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.data.penguin import Penguin
from houdini.data.buddy import IgnoreList, IgnoreListCollection
from houdini.data.penguin import Penguin
from houdini.handlers import Priority, XMLPacket, XTPacket
@handlers.handler(XMLPacket('login'), priority=Priority.Low)

View File

@ -1,11 +1,12 @@
import operator
import time
from aiocache import cached
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.data.item import Item, ItemCollection, PenguinItemCollection
from houdini.data.permission import PenguinPermissionCollection
import time
from aiocache import cached
import operator
from houdini.handlers import Priority, XMLPacket, XTPacket
def get_pin_string_key(_, p, player_id):

View File

@ -1,12 +1,11 @@
from houdini import handlers
from houdini.handlers import XTPacket
import time
from houdini import handlers
from houdini.data import db
from houdini.data.penguin import Penguin
from houdini.data.buddy import IgnoreList
from houdini.data.mail import PenguinPostcard, PostcardCollection
import time
from houdini.data.penguin import Penguin
from houdini.handlers import XTPacket
@handlers.boot

View File

@ -1,11 +1,9 @@
from houdini import handlers
from houdini.commands import has_command_prefix, invoke_command_string
from houdini.data.moderator import ChatFilterRuleCollection
from houdini.handlers import XTPacket
from houdini.handlers.play.moderation import moderator_ban
from houdini.commands import invoke_command_string, has_command_prefix
from houdini.data.moderator import ChatFilterRuleCollection
@handlers.boot
async def filter_load(server):
@ -25,16 +23,17 @@ async def handle_send_message(p, penguin_id: int, message: str):
await penguin.send_xt("mm", message, penguin_id)
return
tokens = message.lower().split()
if p.server.chat_filter_words:
tokens = message.lower().split()
word, consequence = next(((w, c) for w, c in p.server.chat_filter_words.items() if w in tokens))
word, consequence = next(((w, c) for w, c in p.server.chat_filter_words.items() if w in tokens))
if consequence.ban:
return await moderator_ban(p, p.id, comment='Inappropriate language', message=message)
elif consequence.warn:
return
elif consequence.filter:
return
if consequence.ban:
return await moderator_ban(p, p.id, comment='Inappropriate language', message=message)
elif consequence.warn:
return
elif consequence.filter:
return
if has_command_prefix(p.server.config.command_prefix, message):
await invoke_command_string(p.server.commands, p, message)

View File

@ -1,12 +1,12 @@
from houdini import handlers
from houdini.data import db
from houdini.handlers import XTPacket
from houdini.data.moderator import Ban, Warning, Report
from houdini.data.penguin import Penguin
from houdini.constants import ClientType
import datetime
from houdini import handlers
from houdini.constants import ClientType
from houdini.data import db
from houdini.data.moderator import Ban, Report, Warning
from houdini.data.penguin import Penguin
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('o', 'k'))
async def handle_kick_player(p, penguin_id: int):

View File

@ -1,16 +1,15 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.data import db
from houdini.data.music import PenguinTrack, TrackLike
from houdini.constants import ClientType
from houdini.crypto import Crypto
import asyncio
import re
from datetime import date, datetime
from gino.loader import ColumnLoader
from datetime import datetime, date
import asyncio
import re
from houdini import handlers
from houdini.constants import ClientType
from houdini.crypto import Crypto
from houdini.data import db
from houdini.data.music import PenguinTrack, TrackLike
from houdini.handlers import XTPacket
class SoundStudio:

View File

@ -1,23 +1,66 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.data import db
from houdini.data.room import Room
from houdini.data.penguin import Penguin, Login
from houdini.data.room import PenguinIglooRoom, PenguinBackyardRoom, RoomCollection
from houdini.constants import ClientType, StatusField
import hashlib
import random
import time
import pytz
import hashlib
from datetime import date, datetime
import pytz
from houdini import handlers
from houdini.constants import ClientType, StatusField
from houdini.data import db
from houdini.data.penguin import Login, Penguin
from houdini.data.room import PenguinBackyardRoom, PenguinIglooRoom, Room, RoomCollection, RoomTable, RoomWaddle
from houdini.handlers import XTPacket
from houdini.handlers.games.four import ConnectFourLogic
from houdini.handlers.games.mancala import MancalaLogic
from houdini.handlers.games.ninja.card import CardJitsuLogic, SenseiLogic
from houdini.handlers.games.ninja.fire import CardJitsuFireLogic, FireSenseiLogic
from houdini.handlers.games.ninja.water import CardJitsuWaterLogic, WaterSenseiLogic
from houdini.handlers.games.sled import SledRacingLogic
from houdini.handlers.games.treasure import TreasureHuntLogic
TableLogicMapping = {
'four': ConnectFourLogic,
'mancala': MancalaLogic,
'treasure': TreasureHuntLogic
}
WaddleLogicMapping = {
'sled': SledRacingLogic,
'card': CardJitsuLogic,
'sensei': SenseiLogic,
'water': CardJitsuWaterLogic,
'watersensei': WaterSenseiLogic,
'fire': CardJitsuFireLogic,
'firesensei': FireSenseiLogic
}
async def setup_tables(room_collection):
async with db.transaction():
async for table in RoomTable.query.gino.iterate():
room_collection[table.room_id].tables[table.id] = table
table.room = room_collection[table.room_id]
table.logic = TableLogicMapping[table.game]()
async def setup_waddles(room_collection):
async with db.transaction():
async for waddle in RoomWaddle.query.gino.iterate():
room_collection[waddle.room_id].waddles[waddle.id] = waddle
waddle.room = room_collection[waddle.room_id]
waddle.logic = WaddleLogicMapping[waddle.game]
@handlers.boot
async def rooms_load(server):
server.rooms = await RoomCollection.get_collection()
await server.rooms.setup_tables()
await server.rooms.setup_waddles()
await setup_tables(server.rooms)
await setup_waddles(server.rooms)
server.logger.info(f'Loaded {len(server.rooms)} rooms ({len(server.rooms.spawn_rooms)} spawn)')

View File

@ -1,18 +1,5 @@
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.data.ninja import PenguinCardCollection, CardCollection
@handlers.boot
async def cards_load(server):
server.cards = await CardCollection.get_collection()
server.logger.info(f'Loaded {len(server.cards)} ninja cards')
@handlers.handler(XMLPacket('login'), priority=Priority.Low)
@handlers.allow_once
async def load_ninja_inventory(p):
p.cards = await PenguinCardCollection.get_collection(p.id)
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('ni', 'gnr'))
@ -40,7 +27,7 @@ async def handle_get_snow_level(p):
await p.send_xt('gsl', 0, 0)
@handlers.handler(XTPacket('cd', 'gcd'))
@handlers.handler(XTPacket('ni', 'gcd'))
async def handle_get_card_data(p):
await p.send_xt('gcd', '|'.join(f'{card.card_id},{card.quantity},{card.member_quantity}'
for card in p.cards.values()))

View File

@ -1,9 +1,7 @@
from houdini import handlers
from houdini.handlers import XTPacket
import ujson
from houdini import handlers
from houdini.handlers import XTPacket
DefaultPartyCookie = {
'msgViewedArray': [0] * 10,

View File

@ -1,21 +1,16 @@
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.constants import ClientType, StatusField
from houdini.data.pet import PenguinPuffleCollection, PenguinPuffleItemCollection, PenguinPuffle, \
PuffleCollection, PuffleItemCollection, \
PuffleTreasureFurniture, PuffleTreasureItem, \
PuffleTreasurePuffleItem
from houdini.data.room import PenguinBackyardRoom, PenguinIglooRoom
from houdini.data.mail import PenguinPostcard
from datetime import datetime, timedelta
import time
import random
import asyncio
import operator
import random
import time
from datetime import datetime, timedelta
from houdini import handlers
from houdini.constants import ClientType, StatusField
from houdini.data.mail import PenguinPostcard
from houdini.data.pet import PenguinPuffle, PenguinPuffleCollection, PenguinPuffleItemCollection, PuffleCollection, \
PuffleItemCollection, PuffleTreasureFurniture, PuffleTreasureItem, PuffleTreasurePuffleItem
from houdini.data.room import PenguinBackyardRoom, PenguinIglooRoom
from houdini.handlers import Priority, XMLPacket, XTPacket
PuffleKillerInterval = 600
LegacyPuffleIds = [0, 1, 2, 3, 4, 5, 6, 7, 8]
@ -450,6 +445,7 @@ async def handle_wear_puffle(p, item_id: int):
@handlers.disconnected
@handlers.player_attribute(client_type=ClientType.Legacy)
@handlers.player_attribute(joined_world=True)
async def handle_stop_walking(p):
if p.joined_world:
if p.walking:

View File

@ -1,16 +1,17 @@
from houdini import handlers
from houdini.converters import SeparatorConverter
from houdini.handlers import XTPacket, XMLPacket, Priority
from houdini.data import db
from houdini.data.penguin import Penguin, PenguinMembership
from houdini.data.mail import PenguinPostcard
from houdini.constants import ClientType
import asyncio
import random
import time
from datetime import datetime, timedelta
from aiocache import cached
from datetime import datetime, timedelta
import random
import asyncio
import time
from houdini import handlers
from houdini.constants import ClientType
from houdini.converters import SeparatorConverter
from houdini.data import db
from houdini.data.mail import PenguinPostcard
from houdini.data.penguin import Penguin, PenguinMembership
from houdini.handlers import Priority, XMLPacket, XTPacket
def get_player_string_key(_, p, player_id):

View File

@ -1,12 +1,11 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.constants import ClientType
import time
from datetime import datetime, timedelta
import ujson
import time
from houdini import handlers
from houdini.constants import ClientType
from houdini.handlers import XTPacket
RainbowQuestRewards = [6158, 4809, 1560, 3159]
RainbowBonusReward = 5220

View File

@ -1,9 +1,9 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.data.penguin import Penguin
import random
from houdini import handlers
from houdini.data.penguin import Penguin
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('r', 'cdu'))
@handlers.cooldown(1)

View File

@ -1,7 +1,6 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.data.item import Item
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('s', 'upc'))

View File

@ -1,11 +1,11 @@
from houdini import handlers
from houdini.handlers import XMLPacket, XTPacket, Priority
from houdini.handlers.play.navigation import handle_join_server, handle_join_room
from houdini.data.stamp import Stamp, CoverStamp, CoverItem, PenguinStampCollection, StampCollection
from houdini.data.penguin import Penguin
from aiocache import cached
from houdini import handlers
from houdini.data.penguin import Penguin
from houdini.data.stamp import CoverItem, CoverStamp, PenguinStampCollection, Stamp, StampCollection
from houdini.handlers import Priority, XMLPacket, XTPacket
from houdini.handlers.play.navigation import handle_join_room, handle_join_server
def get_book_cover_key(_, p, player_id):
return f'book.{player_id}'

View File

@ -1,6 +1,6 @@
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.constants import ClientType
from houdini.handlers import XTPacket
@handlers.handler(XTPacket('t', 'at'))

View File

@ -1,17 +1,18 @@
from datetime import datetime
from houdini import handlers
from houdini.handlers import XTPacket
from houdini.constants import ClientType
from houdini.data.item import Item
from houdini.data.igloo import Furniture, Igloo
from houdini.data import db
from houdini.data.redemption import RedemptionCode, RedemptionAwardCard, RedemptionAwardFlooring, \
RedemptionAwardFurniture, RedemptionAwardIgloo, RedemptionAwardItem, RedemptionAwardLocation,\
RedemptionAwardPuffle, RedemptionAwardPuffleItem, PenguinRedemptionBook, PenguinRedemptionCode
from houdini.data.redemption import PenguinRedemptionBook, PenguinRedemptionCode, RedemptionAwardCard, \
RedemptionAwardFlooring, RedemptionAwardFurniture, RedemptionAwardIgloo, RedemptionAwardItem, \
RedemptionAwardLocation, RedemptionAwardPuffle, RedemptionAwardPuffleItem, RedemptionCode
from houdini.handlers import XTPacket
import random
from datetime import datetime
@handlers.handler(XTPacket('rjs', ext='red'), pre_login=True, client=ClientType.Vanilla)
@handlers.allow_once
async def handle_join_redemption_server_vanilla(p, credentials: str, confirmation_hash: str, lang: str):
@ -172,9 +173,6 @@ async def handle_golden_choice(p, redemption_code: str, choice: int):
if penguin_redeemed:
return await p.close()
if choice == 1:

View File

@ -1,19 +1,17 @@
import asyncio
import logging
import os
import sys
from houdini.spheniscidae import Spheniscidae
from houdini.penguin import Penguin
from houdini import PenguinStringCompiler
import logging
from logging.handlers import RotatingFileHandler
import aioredis
from aiocache import SimpleMemoryCache, caches
from houdini import PenguinStringCompiler
from houdini.data import db
from houdini.data.permission import PermissionCollection
from houdini.penguin import Penguin
from houdini.spheniscidae import Spheniscidae
try:
import uvloop
@ -32,7 +30,6 @@ from houdini.handlers.play.player import server_heartbeat, server_egg_timer
from houdini.handlers.play.pet import decrease_stats
from houdini.handlers.play.music import SoundStudio
from houdini.handlers.games.dance import DanceFloor
class Houdini:
@ -90,6 +87,9 @@ class Houdini:
self.music = None
self.dance_floor = None
self.match_making = None
self.water_match_making = None
self.fire_match_making = None
self.puck = (0, 0)

View File

@ -1,9 +1,9 @@
import time
from houdini.spheniscidae import Spheniscidae
from houdini.data import penguin
from houdini.data.mail import PenguinPostcard
from houdini.handlers.play.pet import get_my_player_walking_puffle
from houdini.spheniscidae import Spheniscidae
class Penguin(Spheniscidae, penguin.Penguin):
@ -193,15 +193,15 @@ class Penguin(Spheniscidae, penguin.Penguin):
return True
async def add_card(self, card, quantity=1, member_quantity=0):
async def add_card(self, card, quantity=0, member_quantity=0):
if card.id in self.cards:
penguin_card = self.cards[card.id]
await penguin_card.update(
quantity=penguin_card.quantity + quantity,
member_quantity=member_quantity).apply()
member_quantity=penguin_card.member_quantity + member_quantity).apply()
else:
await self.cards.insert(card_id=card.id)
await self.cards.insert(card_id=card.id, quantity=quantity, member_quantity=member_quantity)
self.logger.info(f'{self.username} added \'{card.name}\' to their ninja deck')

View File

@ -1,7 +1,5 @@
from abc import ABC
from abc import abstractmethod
import inspect
from abc import ABC, abstractmethod
from houdini import _AbstractManager, get_package_modules

View File

@ -1,12 +1,10 @@
from houdini.handlers import XMLPacket, XTPacket
from asyncio import IncompleteReadError, CancelledError
import defusedxml.cElementTree as Et
from asyncio import CancelledError, IncompleteReadError
from xml.etree.cElementTree import Element, SubElement, tostring
import defusedxml.cElementTree as Et
from houdini.constants import ClientType
from houdini.handlers import AuthorityError
from houdini.handlers import AuthorityError, XMLPacket, XTPacket
class Spheniscidae: