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

Commit 0b415f8

Browse files
committed
Merge pull request #45 from CarrotsAreMediocre/development
Release 1.1.0
2 parents ae87c2f + 5fc3d90 commit 0b415f8

17 files changed

Lines changed: 246 additions & 165 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ nosetests.xml
3939
*config.json
4040
*.db
4141
*.db-journal
42-
*.log
42+
*.log
43+
.idea/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This requires Python and pip to install, and on *nix systems the python developm
2323
Create a configuration file using the config.json.example. The most important things to note are owner\_uuid, which should be set to a character's UUID that you possess and have never shared; server\_address and server\_port, which should be set to the proxied server. StarryPy will default to port 21025 for normal clients to connect to. Select a good random port, or set it to 21024 and firewall it off from the outside.
2424

2525
# Run it
26-
Use your terminal (cmd or powershell on windows) and `cd` to the directory you installed StarryPy into. Enter `python server.py` to start the proxy.
26+
After making sure the Starbound server is running, use your terminal (cmd or powershell on windows) and `cd` to the directory you installed StarryPy into. Enter `python server.py` to start the proxy.
2727

2828
# Built-in plugins
2929
StarryPy is nearly entirely plugin driven (our plugin manager is a plugin!), so there are quite a few built-in plugins. The truly important plugins are in the core\_plugins folder. If you remove any of those, it's likely that most other plugins will break. We'll break them down by core plugin and normal plugin classes. If you are looking for the commands, feel free to skip the core plugins section.

config.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import logging
3-
3+
import inspect
44

55
class Singleton(type):
66
_instances = {}
@@ -20,30 +20,46 @@ def __init__(self):
2020
try:
2121
with open("config/config.json", "r+") as config:
2222
self.config = json.load(config)
23+
if not "plugin_config" in self.config:
24+
self.config["plugin_config"] = {}
2325
except Exception as e:
24-
self.logger.critical("Tried to save the configuration file, failed.\n%s", str(e))
26+
self.logger.critical("Tried to read the configuration file, failed.\n%s", str(e))
2527
raise
2628
self.logger.debug("Created configuration manager.")
2729

2830
def save(self):
2931
try:
3032
with open("config/config.json", "w") as config:
31-
config.write(json.dumps(self.config, indent=4, separators=(',', ': ')))
33+
config.write(json.dumps(self.config, indent=4, separators=(',', ': '), sort_keys=True))
3234
except Exception as e:
3335
self.logger.critical("Tried to save the configuration file, failed.\n%s", str(e))
3436
raise
3537

3638
def __getattr__(self, item):
37-
if item != "config":
39+
if item == "config":
40+
return super(ConfigurationManager, self).__getattribute__(item)
41+
42+
elif item == "plugin_config":
43+
caller = inspect.stack()[1][0].f_locals["self"].__class__.name
44+
if caller in self.config["plugin_config"]:
45+
return self.config["plugin_config"][caller]
46+
else:
47+
return {}
48+
49+
else:
3850
if item in self.config:
3951
return self.config[item]
4052
else:
53+
self.logger.error("Couldn't find configuration option %s in configuration file.", item)
4154
raise AttributeError
42-
else:
43-
return super(ConfigurationManager, self).__getattribute__(item)
4455

4556
def __setattr__(self, key, value):
46-
if key != "config":
47-
self.config[key] = value
57+
if key == "config":
58+
super(ConfigurationManager, self).__setattr__(key, value)
59+
60+
elif key == "plugin_config":
61+
caller = inspect.stack()[1][0].f_locals["self"].__class__.name
62+
self.config["plugin_config"][caller] = value
63+
4864
else:
49-
super(ConfigurationManager, self).__setattr__(key, value)
65+
self.config[key] = value

config/config.json.example

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,37 @@
11
{
2-
"server_hostname": "localhost",
3-
"debug_file": "debug.log",
2+
"bind_port": 21025,
3+
"chat_prefix": "@",
4+
"colors": {
5+
"admin": "^#C443F7;",
6+
"default": "^#F7EB43;",
7+
"guest": "^#F7EB43;",
8+
"moderator": "^#4385F7;",
9+
"owner": "^#F7434C;",
10+
"registered": "^#A0F743;"
11+
},
12+
"command_prefix": "/",
413
"core_plugin_path": "./core_plugins",
5-
"owner_uuid": "",
14+
"debug_file": "debug.log",
15+
"owner_uuid": "!!! SET THIS !!!",
16+
"passthrough": false,
617
"player_db": "config/player.db",
7-
"server_port": 21024,
18+
"plugin_config": {
19+
"motd_plugin": "Welcome to the server! Play nice.",
20+
"new_player_greeter_plugin": {
21+
"items": [
22+
[
23+
"coalore",
24+
200
25+
],
26+
[
27+
"alienburger",
28+
5
29+
]
30+
],
31+
"message": "Welcome to the server! Here are some starter items to get you off to a good start."
32+
}
33+
},
834
"plugin_path": "./plugins",
9-
"command_prefix": "/",
10-
"passthrough" : false,
11-
"colors": {
12-
"default": "^#F7EB43;",
13-
"guest": "^#F7EB43;",
14-
"registered": "^#A0F743;",
15-
"moderator": "^#4385F7;",
16-
"admin": "^#C443F7;",
17-
"owner": "^#F7434C;"
18-
}
19-
}
35+
"upstream_hostname": "localhost",
36+
"upstream_port": 21024
37+
}

core_plugins/player_manager/plugin.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,12 @@ def after_connect_response(self, data):
7070
connection_parameters = connect_response().parse(data.data)
7171
if not connection_parameters.success:
7272
self.protocol.transport.loseConnection()
73-
self.protocol.player.client_id = connection_parameters.client_id
74-
self.protocol.player.logged_in = True
75-
self.logger.info("Player %s (UUID: %s, IP: %s) logged in" % (
76-
self.protocol.player.name, self.protocol.player.uuid,
77-
self.protocol.transport.getHost().host))
73+
else:
74+
self.protocol.player.client_id = connection_parameters.client_id
75+
self.protocol.player.logged_in = True
76+
self.logger.info("Player %s (UUID: %s, IP: %s) logged in" % (
77+
self.protocol.player.name, self.protocol.player.uuid,
78+
self.protocol.transport.getHost().host))
7879

7980
def after_world_start(self, data):
8081
world_start = packets.Variant("").parse(data.data)

plugins/admin_commands_plugin/admin_command_plugin.py

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from twisted.internet import reactor
12
from base_plugin import SimpleCommandPlugin, BasePlugin
23
from core_plugins.player_manager import permissions, UserLevels
34
from packets import chat_sent
4-
from utility_functions import give_item_to_player
5+
from utility_functions import give_item_to_player, extract_name
56

67

78
class UserCommandPlugin(SimpleCommandPlugin):
@@ -10,44 +11,25 @@ class UserCommandPlugin(SimpleCommandPlugin):
1011
"""
1112
name = "user_management_commands"
1213
depends = ['command_dispatcher', 'player_manager']
13-
commands = ["who", "whois", "promote", "kick", "ban", "give_item", "planet", "mute", "unmute", "passthrough"]
14+
commands = ["who", "whois", "promote", "kick", "ban", "give_item", "planet", "mute", "unmute", "passthrough", "shutdown"]
1415
auto_activate = True
1516

1617
def activate(self):
1718
super(UserCommandPlugin, self).activate()
1819
self.player_manager = self.plugins['player_manager'].player_manager
1920
self.godmode = {}
2021

21-
@staticmethod
22-
def extract_name(l):
23-
name = []
24-
if l[0][0] not in ["'", '"']:
25-
return l[0], l[1:]
26-
name.append(l[0][1:])
27-
terminator = l[0][0]
28-
for idx, s in enumerate(l[1:]):
29-
if s[-1] == terminator:
30-
name.append(s[:-1])
31-
if idx + 2 != len(l):
32-
return " ".join(name), l[idx + 2:]
33-
else:
34-
return " ".join(name), None
35-
else:
36-
name.append(s)
37-
raise ValueError("Final terminator character of <%s> not found" %
38-
terminator)
39-
4022
def who(self, data):
4123
"""Returns all current users on the server. Syntax: /who"""
4224
who = [w.colored_name(self.config.colors) for w in self.player_manager.who()]
43-
self.protocol.send_chat_message("Players online: %s" % " ".join(who))
25+
self.protocol.send_chat_message("%d players online: %s" % (len(who), ", ".join(who)))
4426
return False
4527

4628
def planet(self, data):
4729
"""Displays who is on your current planet."""
4830
who = [w.colored_name(self.config.colors) for w in self.player_manager.who() if
4931
w.planet == self.protocol.player.planet and not w.on_ship]
50-
self.protocol.send_chat_message("Players on your current planet: %s" % " ".join(who))
32+
self.protocol.send_chat_message("%d players on your current planet: %s" % (len(who), ", ".join(who)))
5133

5234
@permissions(UserLevels.ADMIN)
5335
def whois(self, data):
@@ -122,7 +104,7 @@ def make_admin(self, player):
122104
@permissions(UserLevels.MODERATOR)
123105
def kick(self, data):
124106
"""Kicks a user from the server. Usage: /kick [username] [reason]"""
125-
name, reason = self.extract_name(data)
107+
name, reason = extract_name(data)
126108
if reason is None:
127109
reason = "no reason given"
128110
info = self.player_manager.whois(name)
@@ -169,7 +151,7 @@ def unban(self, data):
169151
def give_item(self, data):
170152
"""Gives an item to a player. Syntax: /give [target player] [item name] [optional: item count]"""
171153
if len(data) >= 2:
172-
name, item = self.extract_name(data)
154+
name, item = extract_name(data)
173155
target_player = self.player_manager.get_logged_in_by_name(name)
174156
target_protocol = self.protocol.factory.protocols[target_player.protocol]
175157
if target_player is not None:
@@ -220,11 +202,18 @@ def unmute(self, data):
220202
target_protocol.send_chat_message("You have been unmuted.")
221203
self.protocol.send_chat_message("%s has been unmuted." % name)
222204

223-
@permissions(UserLevels.OWNER)
205+
@permissions(UserLevels.ADMIN)
224206
def passthrough(self, data):
225207
"""Sets the server to passthrough mode. *This is irreversible without restart.* Syntax: /passthrough"""
226208
self.config.passthrough = True
227209

210+
@permissions(UserLevels.ADMIN)
211+
def shutdown(self, data):
212+
"""Shutdown the server in n seconds. Syntax: /shutdown [number of seconds] (>0)"""
213+
x = float(data[0])
214+
self.protocol.factory.broadcast("SERVER ANNOUNCEMENT: Server is shutting down in %s seconds!" % data[0])
215+
reactor.callLater(x, reactor.stop)
216+
228217
class MuteManager(BasePlugin):
229218
name = "mute_manager"
230219
def on_chat_sent(self, data):

plugins/admin_messenger.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from base_plugin import BasePlugin
2-
from core_plugins.player_manager import UserLevels
2+
from core_plugins.player_manager import permissions, UserLevels
33
import packets
44

55

@@ -9,9 +9,16 @@ class AdminMessenger(BasePlugin):
99
depends = ['player_manager']
1010
auto_activate = True
1111

12+
def activate(self):
13+
super(AdminMessenger, self).activate()
14+
self.prefix = self.config.chat_prefix
15+
1216
def on_chat_sent(self, data):
1317
data = packets.chat_sent().parse(data.data)
14-
if data.message[:2] == "##":
18+
if data.message[:3] == self.prefix*3:
19+
self.broadcast_message(data)
20+
return False
21+
if data.message[:2] == self.prefix*2:
1522
self.message_admins(data)
1623
return False
1724
return True
@@ -24,3 +31,10 @@ def message_admins(self, message):
2431
message.message[2:]))
2532
self.logger.info("Received an admin message from %s. Message: %s", self.protocol.player.name,
2633
message.message[2:])
34+
35+
@permissions(UserLevels.ADMIN)
36+
def broadcast_message(self, message):
37+
for protocol in self.protocol.factory.protocols.itervalues():
38+
protocol.send_chat_message("%sSERVER BROADCAST: %s%s" % (self.config.colors["admin"], message.message[3:], self.config.colors["default"]))
39+
self.logger.info("Broadcast from %s. Message: %s", self.protocol.player.name,
40+
message.message[3:])

plugins/motd_plugin/motd.txt

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

plugins/motd_plugin/motd_plugin.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ class MOTDPlugin(SimpleCommandPlugin):
1515
def activate(self):
1616
super(MOTDPlugin, self).activate()
1717
try:
18-
with open("plugins/motd_plugin/motd.txt") as motd:
19-
self._motd = motd.read()
18+
self._motd = self.config.plugin_config
2019
except:
21-
self.logger.error("Couldn't read message of the day from file.")
20+
self.logger.error("Couldn't read message of the day from config.")
2221
raise
2322

2423
def after_connect_response(self, data):
@@ -39,8 +38,7 @@ def set_motd(self, motd):
3938
"""Sets the message of the day to a new value. Usage: /set_motd [New message of the day]"""
4039
try:
4140
self._motd = " ".join(motd)
42-
with open("plugins/motd_plugin/motd.txt", "w") as f:
43-
f.write(self._motd.encode("utf-8"))
41+
self.config.plugin_config = self._motd
4442
self.logger.info("MOTD changed to: %s", self._motd)
4543
self.send_motd()
4644
except:

plugins/new_player_greeter_plugin/new_player_greeter_plugin.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ class NewPlayerGreeter(BasePlugin):
1111

1212
def activate(self):
1313
super(NewPlayerGreeter, self).activate()
14-
self.starter_items = []
15-
with open("plugins/new_player_greeter_plugin/starter_items.txt") as f:
16-
for item, count in [x.split(" ") for x in f]:
17-
self.starter_items.append((item, count))
1814

1915
def after_connect_response(self, data):
2016
try:
@@ -30,9 +26,8 @@ def after_connect_response(self, data):
3026
self.logger.info("Gave starter items to %s.", self.protocol.player.name)
3127

3228
def give_items(self):
33-
for item in self.starter_items:
29+
for item in self.config.plugin_config["items"]:
3430
give_item_to_player(self.protocol, item[0], item[1])
3531

3632
def send_greetings(self):
37-
with open("plugins/new_player_greeter_plugin/new_player_message.txt") as f:
38-
self.protocol.send_chat_message(f.read())
33+
self.protocol.send_chat_message(self.config.plugin_config["message"])

0 commit comments

Comments
 (0)