Skip to content
This repository was archived by the owner on Apr 27, 2019. It is now read-only.

Commit ccc239e

Browse files
committed
Merge pull request #148 from 8r2y5/feature/142
Merging PR #148, Optimalization, unit tests.
2 parents c1a5ffc + 60845cd commit ccc239e

19 files changed

Lines changed: 768 additions & 84 deletions

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.cache
12
*.py[cod]
23

34
# C extensions
@@ -50,3 +51,6 @@ nosetests.xml
5051

5152
# Mac cleanup
5253
.DS_Store
54+
55+
cover
56+
.noseids

__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

base_plugin.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
11
# encoding: utf-8
2+
import re
3+
4+
from packets import Packets
5+
6+
7+
packet_name_regex = re.compile(r'(?P<when>on|after)_(?P<packet_name>\w+)')
8+
9+
10+
class MapOverridePacketsMethods(type):
11+
ignored_methods = ('activate', 'deactivate')
12+
13+
def __new__(cls, name, bases, cls_dict):
14+
if name != 'BasePlugin':
15+
cls_dict['overridden_packets'] = {}
16+
methods = (
17+
key for key, value in cls_dict.iteritems()
18+
if key not in cls.ignored_methods and callable(value)
19+
)
20+
for packet_method_name in methods:
21+
packet = packet_name_regex.match(packet_method_name)
22+
if packet:
23+
packet_name = packet.group('packet_name').upper()
24+
enum = getattr(Packets, packet_name, None)
25+
if enum:
26+
cls_dict['overridden_packets'].setdefault(
27+
enum.value, {}
28+
)[packet.group('when')] = packet_method_name
29+
30+
return super(MapOverridePacketsMethods, cls).__new__(
31+
cls, name, bases, cls_dict
32+
)
233

334

435
class BasePlugin(object):
@@ -19,17 +50,30 @@ class BasePlugin(object):
1950
complain quite thoroughly.
2051
"""
2152

53+
__metaclass__ = MapOverridePacketsMethods
54+
2255
name = 'Base Plugin'
2356
description = 'The common class for all plugins to inherit from.'
2457
version = '.1'
2558
depends = []
59+
active = False
60+
61+
def __init__(self, *args, **kwargs):
62+
self.overridden_methods = {}
63+
super(BasePlugin, self).__init__(*args, **kwargs)
64+
if self.__class__.__name__ != 'BasePlugin':
65+
for packet, when_dict in self.overridden_packets.iteritems():
66+
self.overridden_methods.setdefault(packet, {})
67+
for when, packet_name in when_dict.iteritems():
68+
self.overridden_methods[packet][when] = getattr(
69+
self, packet_name
70+
)
2671

2772
def activate(self):
2873
"""
2974
Called when the plugins are activated, do any setup work here.
3075
"""
3176
self.active = True
32-
self.logger.debug('%s plugin object activated.', self.name)
3377
return True
3478

3579
def deactivate(self):
@@ -38,7 +82,6 @@ def deactivate(self):
3882
as it is likely that the plugin will soon be destroyed.
3983
"""
4084
self.active = False
41-
self.logger.debug('%s plugin object deactivated', self.name)
4285
return True
4386

4487
def on_protocol_version(self, data):
@@ -371,11 +414,14 @@ def after_fly_ship(self, data):
371414
def after_central_structure_update(self, data):
372415
return True
373416

374-
def __repr__(self):
375-
return '<Plugin instance: {} (version {})>'.format(
417+
def __unicode__(self):
418+
return u'<Plugin instance: {} (version {})>'.format(
376419
self.name, self.version
377420
)
378421

422+
def __str__(self):
423+
return self.__unicode__()
424+
379425

380426
class CommandNameError(Exception):
381427
"""

config.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,16 @@
55
import sys
66
import os
77

8-
from utility_functions import recursive_dictionary_update, path
9-
10-
11-
class Singleton(type):
12-
_instances = {}
13-
14-
def __call__(cls, *args, **kwargs):
15-
if cls not in cls._instances:
16-
cls._instances[cls] = super(Singleton, cls).__call__(
17-
*args, **kwargs
18-
)
19-
return cls._instances[cls]
8+
from utility_functions import (
9+
recursive_dictionary_update,
10+
path,
11+
Singleton
12+
)
2013

2114

2215
class ConfigurationManager(object):
2316
__metaclass__ = Singleton
17+
2418
logger = logging.getLogger('starrypy.config.ConfigurationManager')
2519
log_format = logging.Formatter(
2620
'%(asctime)s - %(levelname)s - %(name)s # %(message)s'

plugin_manager.py

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(self, factory, base_class=BasePlugin):
5252
5353
:param base_class: The base class to use while searching for plugins.
5454
"""
55+
self.packets = {}
5556
self.plugins = {}
5657
self.plugin_classes = {}
5758
self.plugins_waiting_to_load = {}
@@ -65,6 +66,7 @@ def __init__(self, factory, base_class=BasePlugin):
6566
self.plugin_dir = path.child(self.config.plugin_path)
6667
sys.path.append(self.plugin_dir.path)
6768

69+
def prepare(self):
6870
self.load_plugins(
6971
[
7072
'core.admin_commands_plugin',
@@ -87,23 +89,18 @@ def __init__(self, factory, base_class=BasePlugin):
8789

8890
def installed_plugins(self):
8991
"""
90-
Get list of all plugins in the plugin_dir.
91-
92-
:param name: None
93-
:return: Array of plugin names.
92+
Returns list of all plugins in the plugin_dir.
9493
"""
95-
installed_plugins = filter(
96-
None,
94+
return filter(
95+
lambda name: not (name is None or name == 'core'),
9796
(
98-
self.get_plugin_name_from_file(f)
99-
for f in self.plugin_dir.globChildren('*')
97+
self.get_plugin_name_from_file(plugin_file)
98+
for plugin_file in self.plugin_dir.globChildren('*')
10099
)
101100
)
102101

103-
# don't list core as a plugin
104-
return filter(lambda name: name != 'core', installed_plugins)
105-
106-
def get_plugin_name_from_file(self, f):
102+
@staticmethod
103+
def get_plugin_name_from_file(f):
107104
if f.isdir():
108105
return f.basename()
109106
else:
@@ -114,7 +111,6 @@ def import_plugin(self, name):
114111
Import plugin that has the given name, and is a subclass of base_class.
115112
116113
:param name: The name of the plugin to import.
117-
:return: None
118114
"""
119115
try:
120116
mod = __import__(name, globals(), locals(), [], 0)
@@ -221,6 +217,7 @@ def activate_plugins(self, plugins, dependencies):
221217
for p in plugin_deps:
222218
self.plugin_classes[plugin.name].plugins[p.name] = p
223219
self.plugins[plugin.name].activate()
220+
self.map_plugin_packets(plugin)
224221
except FatalPluginError as e:
225222
self.logger.critical(
226223
'A plugin reported a fatal error. Error: %s', str(e)
@@ -236,8 +233,9 @@ def deactivate_plugins(self):
236233
'A plugin reported a fatal error. Error: %s', str(e)
237234
)
238235
raise
236+
self.de_map_plugin_packets(plugin)
239237

240-
def do(self, protocol, command, data):
238+
def do(self, protocol, when, data):
241239
"""
242240
Runs a command across all currently loaded plugins.
243241
@@ -248,28 +246,52 @@ def do(self, protocol, command, data):
248246
:return: Whether or not all plugins returned True or None.
249247
:rtype: bool
250248
"""
251-
return_values = []
252249
if protocol is None:
253250
return True
254-
for plugin in self.plugins.itervalues():
251+
252+
return_values = []
253+
packets = self.packets.get(data.id, {}).get(when, {}).itervalues()
254+
for plugin, packet_method in packets:
255255
try:
256-
if not plugin.active:
257-
continue
258256
plugin.protocol = protocol
259-
res = getattr(plugin, command, lambda _: True)(data)
260-
if res is None:
257+
res = packet_method(data)
258+
if res is False:
259+
return False
260+
elif res is None:
261261
res = True
262+
262263
return_values.append(res)
263264
except:
264265
self.logger.exception(
265266
'Error in plugin %s with function %s.',
266-
str(plugin), command
267+
str(plugin), packet_method.__name__
267268
)
268269
return all(return_values)
269270

270271
def die(self):
271272
self.deactivate_plugins()
272273

274+
def map_plugin_packets(self, plugin):
275+
"""
276+
Maps plugin overridden packets ready to use in do method.
277+
"""
278+
for packet_id, when_dict in plugin.overridden_methods.iteritems():
279+
for when, packet_method in when_dict.iteritems():
280+
self.packets.setdefault(
281+
packet_id, {}
282+
).setdefault(
283+
when, {}
284+
)[plugin.name] = (plugin, packet_method)
285+
286+
def de_map_plugin_packets(self, plugin):
287+
"""
288+
Removes plugin overridden packets method from packets dictionary.
289+
"""
290+
for packet_id, when_dict in self.packets.iteritems():
291+
for when, plugins in when_dict.iteritems():
292+
if plugin.name in plugins:
293+
plugins.pop(plugin.name)
294+
273295

274296
def route(func):
275297
"""
@@ -278,18 +300,15 @@ def route(func):
278300
logger = logging.getLogger('starrypy.plugin_manager.route')
279301

280302
def wrapped_function(self, data):
281-
name = func.__name__
282-
on = 'on_%s' % name
283-
after = 'after_%s' % name
284-
res = self.plugin_manager.do(self, on, data)
303+
res = self.plugin_manager.do(self, 'on', data)
285304
if res:
286305
res = func(self, data)
287306
d = deferLater(
288307
reactor,
289308
.01,
290309
self.plugin_manager.do,
291310
self,
292-
after,
311+
'after',
293312
data
294313
)
295314
d.addErrback(print_this_defered_failure)

plugins/admin_messenger/admin_messenger.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class AdminMessenger(BasePlugin):
99
"""
1010
Adds support to message moderators/admins/owner with a @@ prefixed message.
1111
"""
12-
name = "admin_messenger"
12+
name = 'admin_messenger'
1313
depends = ['player_manager_plugin']
1414

1515
def activate(self):
@@ -26,48 +26,56 @@ def on_chat_sent(self, data):
2626
return False
2727
return True
2828

29-
def message_admins(self, message):
30-
now = datetime.now()
29+
def add_timestamp(self, add_normalizer=False):
3130
if self.config.chattimestamps:
32-
timestamp = "^red;<{}> ^yellow;".format(now.strftime("%H:%M"))
31+
now = datetime.now()
32+
timestamp = '^red;<{}> '.format(now.strftime('%H:%M'))
33+
if add_normalizer:
34+
return '{}^yellow;'.format(timestamp)
35+
return timestamp
3336
else:
34-
timestamp = ""
37+
return ''
38+
39+
def message_admins(self, message):
40+
timestamp = self.add_timestamp(add_normalizer=True)
41+
message = message.message[2:].decode('utf-8')
42+
3543
for protocol in self.factory.protocols.itervalues():
3644
if protocol.player.access_level >= UserLevels.MODERATOR:
3745
protocol.send_chat_message(
38-
"{}{}ADMIN: ^yellow;<{}^yellow;> {}{}".format(
39-
timestamp,
40-
self.config.colors["moderator"],
41-
self.protocol.player.colored_name(self.config.colors),
42-
self.config.colors["moderator"],
43-
message.message[2:].decode("utf-8")
46+
'{timestamp}{moderator_colors}'
47+
'ADMIN: ^yellow;<{player_colors}^yellow;> '
48+
'{moderator_colors}{message}'.format(
49+
timestamp=timestamp,
50+
moderator_colors=self.config.colors['moderator'],
51+
player_colors=(
52+
self.protocol.player.colored_name(
53+
self.config.colors
54+
)
55+
),
56+
message=message
4457
)
4558
)
4659
self.logger.info(
47-
"Received an admin message from %s. Message: %s",
48-
self.protocol.player.name,
49-
message.message[2:].decode("utf-8")
60+
'Received an admin message from %s. Message: %s',
61+
self.protocol.player.name, message
5062
)
5163

5264
@permissions(UserLevels.ADMIN)
5365
def broadcast_message(self, message):
54-
now = datetime.now()
55-
if self.config.chattimestamps:
56-
timestamp = "^red;<{}> ".format(now.strftime("%H:%M"))
57-
else:
58-
timestamp = ""
66+
timestamp = self.add_timestamp()
5967

6068
for protocol in self.factory.protocols.itervalues():
6169
protocol.send_chat_message(
62-
"{}{}BROADCAST: ^red;{}{}".format(
70+
'{}{}BROADCAST: ^red;{}{}'.format(
6371
timestamp,
64-
self.config.colors["admin"],
65-
message.message[3:].decode("utf-8").upper(),
66-
self.config.colors["default"]
72+
self.config.colors['admin'],
73+
message.message[3:].decode('utf-8').upper(),
74+
self.config.colors['default']
6775
)
6876
)
6977
self.logger.info(
70-
"Broadcast from %s. Message: %s",
78+
'Broadcast from %s. Message: %s',
7179
self.protocol.player.name,
72-
message.message[3:].decode("utf-8").upper()
80+
message.message[3:].decode('utf-8').upper()
7381
)

0 commit comments

Comments
 (0)