From 4288248864f9877f14fa08e1d6235998da0b8031 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 17 May 2020 03:21:34 +0100 Subject: [PATCH] Custom attributes for players and plugins --- houdini.sql | 28 ++++++++++++++++++++++++++++ houdini/__init__.py | 6 ++++++ houdini/data/penguin.py | 1 + houdini/data/plugin.py | 30 ++++++++++++++++++++++++++++++ houdini/handlers/play/item.py | 2 ++ houdini/penguin.py | 22 +++++++++++++++++++++- houdini/plugins/__init__.py | 20 ++++++++++++++++++++ 7 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 houdini/data/plugin.py diff --git a/houdini.sql b/houdini.sql index e684122..a3d8af1 100644 --- a/houdini.sql +++ b/houdini.sql @@ -722,6 +722,34 @@ COMMENT ON TABLE penguin_permission IS 'Penguin permissions'; COMMENT ON COLUMN penguin_permission.penguin_id IS 'Penguin ID'; COMMENT ON COLUMN penguin_permission.permission_id IS 'Penguin permission ID'; +DROP TABLE IF EXISTS penguin_attribute; +CREATE TABLE penguin_attribute ( + name TEXT NOT NULL, + penguin_id INT NOT NULL, + value TEXT NOT NULL, + PRIMARY KEY (name, penguin_id), + CONSTRAINT penguin_attribute_ibfk_1 FOREIGN KEY (penguin_id) REFERENCES penguin(id) ON DELETE CASCADE ON UPDATE CASCADE +); + +COMMENT ON TABLE penguin_attribute IS 'Custom penguin attributes'; + +COMMENT ON COLUMN penguin_attribute.name IS 'Attribute unique identifier'; +COMMENT ON COLUMN penguin_attribute.penguin_id IS 'Penguin ID'; +COMMENT ON COLUMN penguin_attribute.value IS 'Value of attribute'; + +DROP TABLE IF EXISTS plugin_attribute; +CREATE TABLE plugin_attribute ( + name TEXT NOT NULL, + plugin_name TEXT NOT NULL, + value TEXT NOT NULL, + PRIMARY KEY (name, plugin_name) +); + +COMMENT ON TABLE plugin_attribute IS 'Custom plugin attributes'; + +COMMENT ON COLUMN plugin_attribute.name IS 'Attribute unique identifier'; +COMMENT ON COLUMN plugin_attribute.plugin_name IS 'Name of plugin attribute belongs to'; +COMMENT ON COLUMN plugin_attribute.value IS 'Value of attribute'; DROP TABLE IF EXISTS report; CREATE TABLE report ( diff --git a/houdini/__init__.py b/houdini/__init__.py index b853c3e..3f604a5 100644 --- a/houdini/__init__.py +++ b/houdini/__init__.py @@ -114,6 +114,12 @@ class PenguinStringCompiler(OrderedDict): return getattr(p, attribute_name) or 0 return attribute_method + @classmethod + def custom_attribute_by_name(cls, attribute_name): + async def attribute_method(p): + return p.get_custom_attribute(attribute_name, 0) + return attribute_method + @classmethod def setup_default_builder(cls, string_builder): string_builder.update({ diff --git a/houdini/data/penguin.py b/houdini/data/penguin.py index 4a8a472..9e4a658 100644 --- a/houdini/data/penguin.py +++ b/houdini/data/penguin.py @@ -84,6 +84,7 @@ class Penguin(db.Model): def __init__(self, *args, **kwargs): self.inventory = None self.permissions = None + self.attributes = None self.igloos = None self.igloo_rooms = None self.furniture = None diff --git a/houdini/data/plugin.py b/houdini/data/plugin.py new file mode 100644 index 0000000..26be11e --- /dev/null +++ b/houdini/data/plugin.py @@ -0,0 +1,30 @@ +from houdini.data import db, AbstractDataCollection + + +class PluginAttribute(db.Model): + __tablename__ = 'plugin_attribute' + + plugin_name = db.Column(db.Text, primary_key=True, nullable=False) + name = db.Column(db.Text, primary_key=True, nullable=False) + value = db.Column(db.Text) + + +class PenguinAttribute(db.Model): + __tablename__ = 'penguin_attribute' + + penguin_id = db.Column(db.ForeignKey('penguin.id', ondelete='CASCADE', onupdate='CASCADE'), primary_key=True, + nullable=False) + name = db.Column(db.Text, primary_key=True, nullable=False) + value = db.Column(db.Text) + + +class PenguinAttributeCollection(AbstractDataCollection): + __model__ = PenguinAttribute + __indexby__ = 'name' + __filterby__ = 'penguin_id' + + +class PluginAttributeCollection(AbstractDataCollection): + __model__ = PluginAttribute + __indexby__ = 'name' + __filterby__ = 'plugin_name' diff --git a/houdini/handlers/play/item.py b/houdini/handlers/play/item.py index 0dd9378..25016aa 100644 --- a/houdini/handlers/play/item.py +++ b/houdini/handlers/play/item.py @@ -4,6 +4,7 @@ import time from houdini import handlers from houdini.data.item import Item, ItemCollection, PenguinItemCollection from houdini.data.permission import PenguinPermissionCollection +from houdini.data.plugin import PenguinAttributeCollection from houdini.handlers import Priority, XMLPacket, XTPacket @@ -45,6 +46,7 @@ async def items_load(server): async def load_inventory(p): p.inventory = await PenguinItemCollection.get_collection(p.id) p.permissions = await PenguinPermissionCollection.get_collection(p.id) + p.attributes = await PenguinAttributeCollection.get_collection(p.id) if p.color is not None and p.color not in p.inventory: await p.inventory.insert(item_id=p.color) diff --git a/houdini/penguin.py b/houdini/penguin.py index 5313c30..a70cc02 100644 --- a/houdini/penguin.py +++ b/houdini/penguin.py @@ -263,8 +263,28 @@ class Penguin(Spheniscidae, penguin.Penguin): async def add_permission(self, permission): if permission not in self.permissions: await self.permissions.insert(name=permission) + def get_custom_attribute(self, name, default=None): + penguin_attribute = self.attributes.get(name, default) + if penguin_attribute == default: + return default + return penguin_attribute.value - self.logger.info(f'{self.username} was assigned permission \'{permission}\'') + async def set_custom_attribute(self, name, value): + if name not in self.attributes: + await self.attributes.insert(name=name, value=value) + else: + attribute = self.attributes[name] + await attribute.update(value=value).apply() + + self.logger.info(f'{self.username} set custom attribute \'{name}\' to \'{value}\'') + + return True + + async def delete_custom_attribute(self, name): + if name in self.attributes: + await self.attributes.delete(name) + + self.logger.info(f'{self.username} deleted attribute \'{name}\'') return True diff --git a/houdini/plugins/__init__.py b/houdini/plugins/__init__.py index de5f81e..cb228f1 100644 --- a/houdini/plugins/__init__.py +++ b/houdini/plugins/__init__.py @@ -2,6 +2,7 @@ import inspect from abc import ABC, abstractmethod from houdini import _AbstractManager, get_package_modules +from houdini.data.plugin import PluginAttributeCollection class IPlugin(ABC): @@ -28,9 +29,27 @@ class IPlugin(ABC): async def ready(self): """Called when the plugin is ready to function.""" + def get_attribute(self, name, default=None): + plugin_attribute = self.attributes.get(name, default) + if plugin_attribute == default: + return default + return plugin_attribute.value + + async def set_attribute(self, name, value): + if name not in self.attributes: + await self.attributes.insert(name=name, value=value) + else: + plugin_attribute = self.attributes.get(name) + await plugin_attribute.update(value=value).apply() + + async def delete_attribute(self, name): + if name in self.attributes: + await self.attributes.delete(name) + @abstractmethod def __init__(self, server): self.server = server + self.attributes = None class PluginManager(_AbstractManager): @@ -58,6 +77,7 @@ class PluginManager(_AbstractManager): await self.server.xml_listeners.load(plugin_object) await self.server.dummy_event_listeners.load(plugin_object) + plugin_object.attributes = await PluginAttributeCollection.get_collection(plugin_index) await plugin_object.ready()