Skip to content

Commit

Permalink
Build dev 28 | Checking off more things on the to-do list. Adding mor…
Browse files Browse the repository at this point in the history
…e finishing touches, should be ready for primetime. I also added a new scripting system and auto-reboot thing.
  • Loading branch information
benbaptist committed Oct 9, 2014
1 parent 28aff5e commit 5abdf20
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 31 deletions.
16 changes: 5 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,12 @@
<ul>
<li>If I make it a separate project, it might use Wrapper.py as a backend</li>
</ul>
- Proxy system (like Bungeecord, perhaps)
<ul>
<strike>
<li> Add more custom in-game Wrapper.py commands such as /halt</li>
<li> Log player actions such as block manipulation, etc.</li>
</strike>
<li>The above is possible now with the proxy mode, using plugins.</li>
</ul>
- Ability to halt server without shutting down wrapper - for fine server control
- Potentially implement region-fixer in Wrapper.py
- Update version of Minecraft server automatically
- Update Wrapper.py automatically or with a one-click update
- First-run setup wizard for new setups
- Potentially implement a way to reload the config - but that might be too difficult/bug prone
- Add permissions system
- Improve configuration system/redo from scratch
<ul>
<li>Add support for comments</li>
Expand All @@ -36,7 +27,6 @@
- Finish adding all block IDs, item IDs and their respective damage values to items.py
- Proxy mode error: Error -3 while decompressing data: incorrect header check
- Proxy mode error: Error -5 while decompressing data: incomplete or truncated stream
- Negative Position packets are completely screwed up
- The server.py and irc.py code SERIOUSLY needs a total rewrite. (I noticed this while fixing pre-1.7 support)
- Duplicate IRC messages (possibily fixed)
- Make URLs posted in IRC clickable inside of Minecraft
Expand All @@ -60,7 +50,11 @@
- Removed IRC -> Server Line-Wrapping (each message was divided automatically every 80 characters - it was annoying)
- Fixed bug where serious plugin errors resulted in that plugin not being reloadable
- Fixed quit messages not being displayed in IRC (finally!)

- Added new shell scripting setting where you can execute certain shell scripts on specific events (NIX-only systems)
<ul>
<li> The schell scripts' are pregenerated with content, and has a short description of when each script is executed, and what arguments are passed to the script, if any </li>
<li> Shell scripts are in wrapper-data/scripts </li>
</ul>

<h4>0.6.0</h4>
- Added an in-development plugin system! Super early, but it works great, it seems.
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ It isn't finished, nor is it pretty, but it should help give you an idea of the

#Features#
Wrapper.py supports the following features:
- Plugin system for modifying the Wrapper or adding Bukkit-like features to a vanilla server
<ul>
<li> Proxy mode allows you to add extra functionality to plugins, such as real /commands</li>
<li> Permissions system with group support </li>
</ul>
- Automatic Backups
<ul>
<li>Automatically delete the oldest backups once you reach amount of backups</li>
Expand All @@ -45,10 +50,6 @@ Wrapper.py supports the following features:
<li> Achievements, deaths, and whatnot appear on IRC</li>
<li> Chat between Minecraft server and IRC channels</li>
</ul>
- Plugin system for modifying the Wrapper or adding Bukkit-like features to a vanilla server
<ul>
<li> Proxy mode allows you to add extra functionality to plugins, such as real /commands</li>
</ul>
- Minecraft 1.7 and later support (uses tellraw!)

#API#
Expand Down
Binary file modified Wrapper.py
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/version.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version": [0, 7, 0], "build": 26}
{"version": [0, 7, 0], "type": "dev", "build": 28}
31 changes: 26 additions & 5 deletions src/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from irc import IRC
from server import Server
from importlib import import_module
from scripts import Scripts
import importlib
from api import API

Expand Down Expand Up @@ -61,6 +62,7 @@ def unloadPlugin(self, plugin):
self.plugins[plugin]["main"].onDisable()
except:
self.log.error("Error while disabling plugin '%s'" % plugin)
self.log.getTraceback()
reload(self.plugins[plugin]["module"])
def loadPlugins(self):
self.log.info("Loading plugins...")
Expand All @@ -77,6 +79,10 @@ def loadPlugins(self):
self.log.error("Failed to import plugin '%s'" % i)
self.plugins[i] = {"name": i, "good": False}
self.callEvent("helloworld.event", {"testValue": True})
def disablePlugins(self):
self.log.error("Disabling plugins...")
for i in self.plugins:
self.unloadPlugin(i)
def reloadPlugins(self):
for i in self.plugins:
try:
Expand Down Expand Up @@ -114,8 +120,8 @@ def callEvent(self, event, payload):
def playerCommand(self, payload):
self.log.info("%s executed: /%s %s" % (str(payload["player"]), payload["command"], " ".join(payload["args"])))
if payload["command"] == "wrapper":
player = payload["player"]
player.message({"text": "Wrapper.py Version %s (build #%d)" % (Config.version, globals.build), "color": "gray", "italic": True})
buildString = self.getBuildString()
player.message({"text": "Wrapper.py Version %s" % (buildString), "color": "gray", "italic": True})
return False
if payload["command"] in ("plugins", "pl"):
player = payload["player"]
Expand Down Expand Up @@ -351,24 +357,37 @@ def start(self):
consoleDaemon.daemon = True
consoleDaemon.start()

if self.config["General"]["shell-scripts"]:
if os.name in ("posix", "mac"):
self.scripts = Scripts(self)
else:
self.log.error("Sorry, but shell scripts only work on *NIX-based systems! If you are using a *NIX-based system, please file a bug report.")

if self.config["Proxy"]["proxy-enabled"]:
t = threading.Thread(target=self.startProxy, args=())
t.daemon = True
t.start()
self.server.startServer()

self.disablePlugins()
def startProxy(self):
if proxy.IMPORT_SUCCESS:
self.proxy = proxy.Proxy(self)
proxyThread = threading.Thread(target=self.proxy.host, args=())
proxyThread.daemon = True
proxyThread.start()
else:
self.log.error("Proxy mode could not be started because you do not have the required modules installed: pycrypt")
self.log.error("Proxy mode could not be started because you do not have one or more of the following modules installed: pycrypt and requests")
def SIGINT(self, s, f):
self.shutdown()
def shutdown(self):
self.halt = True
sys.exit(0)
def getBuildString(self):
if globals.type == "dev":
return "%s (development build #%d)" % (Config.version, globals.build)
else:
return "%s (stable)" % Config.version
def console(self):
while not self.halt:
input = raw_input("")
Expand Down Expand Up @@ -416,20 +435,21 @@ def args(i):
self.log.info("/start & /stop - start and stop the server without auto-restarting respectively without shutting down Wrapper.py")
self.log.info("/restart - restarts the server, obviously")
self.log.info("/halt - shutdown Wrapper.py completely")
self.log.info("Wrapper.py version %s" % Config.version)
self.log.info("Wrapper.py Version %s" % self.getBuildString())
else:
self.log.error("Invalid command %s" % command)
if __name__ == "__main__":
wrapper = Wrapper()
log = wrapper.log
log.info("Wrapper.py started - version %s" % Config.version)
log.info("Wrapper.py started - Version %s" % wrapper.getBuildString())

try:
t = threading.Thread(target=wrapper.start(), args=())
t.daemon = True
t.start()
except SystemExit:
#log.error("Wrapper.py received SystemExit")
wrapper.disablePlugins()
wrapper.halt = True
try:
for player in wrapper.server.players:
Expand All @@ -444,6 +464,7 @@ def args(i):
for line in traceback.format_exc().split("\n"):
log.error(line)
wrapper.halt = True
wrapper.disablePlugins()
try:
for player in wrapper.server.players:
wrapper.server.run("kick %s Wrapper.py crashed - please contact a server admin instantly" % player)
Expand Down
10 changes: 8 additions & 2 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
DEFAULT_CONFIG = """[General]
command = java -jar minecraft_server.1.7.10.jar nogui
auto-restart = True
debug = False
pre-1.7-mode = False
timed-reboot = False
timed-reboot-seconds = 86400
debug = False
shell-scripts = False
[Backups]
;; Automatic backups with automatic backup pruning. Interval is in seconds. ;;
Expand Down Expand Up @@ -78,7 +81,10 @@ def loadConfig(self):
"command": "java -jar minecraft_server.1.7.10.jar",
"auto-restart": True,
"debug": False,
"pre-1.7-mode": False
"pre-1.7-mode": False,
"timed-reboot": False,
"timed-reboot-seconds": 86400,
"shell-scripts": False
},
"IRC":{
"enabled": True,
Expand Down
3 changes: 2 additions & 1 deletion src/globals.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build=26
build=28
type='dev'
2 changes: 1 addition & 1 deletion src/irc.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def msg(string):
msg("kill - force server restart without clean shutdown - only use when server is unresponsive")
msg("start/restart/stop - start the server/automatically stop and start server/stop the server without shutting down Wrapper")
msg('status - show status of the server')
msg("Wrapper.py version %s by benbaptist" % Config.version)
msg("Wrapper.py Version %s by benbaptist" % self.wrapper.getBuildString())
#msg('console - toggle console output to this private message')
elif args(0) == 'togglebackups':
self.config["Backups"]["enabled"] = not self.config["Backups"]["enabled"]
Expand Down
6 changes: 5 additions & 1 deletion src/log.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import time
import time, traceback
from config import Config
class Log:
def __init__(self):
Expand All @@ -19,6 +19,10 @@ def error(self, string):
def debug(self, string):
if Config.debug:
self.prefix("DEBUG", string)
def getTraceback(self):
for line in traceback.format_exc().split("\n"):
if len(line.strip()) > 0: # Remove empty lines
self.error(line)
class PluginLog:
def __init__(self, log, PluginName="Hello"):
self.log = log
Expand Down
10 changes: 7 additions & 3 deletions src/proxy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# I'll probably split this file into more parts later on, like such:
# proxy folder: __init__.py (Proxy), client.py (Client), server.py (Server), network.py (Packet), bot.py (will contain Bot, for bot code)
# this could definitely use some code-cleaning.
import socket, threading, struct, StringIO, time, traceback, json, random, hashlib, os, zlib, binascii, uuid, md5, storage, world
import socket, threading, struct, StringIO, time, traceback, json, random, hashlib, os, zlib, binascii, uuid, md5, storage, world, config
try: # Weird system for handling non-standard modules
import encryption, requests
IMPORT_SUCCESS = True
Expand Down Expand Up @@ -228,6 +228,9 @@ def parse(self, id):
self.version = data["version"]
self.wrapper.server.protocolVersion = self.version
self.packet.version = self.version
if not self.wrapper.server.status == 2:
self.disconnect("Server has not finished booting. Please try connecting again in a few seconds")
return
if data["state"] in (1, 2):
self.state = data["state"]
else:
Expand Down Expand Up @@ -470,8 +473,9 @@ def connect(self):
t.daemon = True
t.start()
def close(self, reason="Disconnected"):
print "Last packet IDs (Server->Client) before disconnection:"
print self.lastPacketIDs
if Config.debug:
print "Last packet IDs (Server->Client) before disconnection:"
print self.lastPacketIDs
self.abort = True
self.packet = None
try:
Expand Down
7 changes: 7 additions & 0 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, args, log, config, wrapper):
self.data = ""
self.backups = []
self.oldServer = []
self.bootTime = time.time()

self.worldName = None
self.protocolVersion = -1 # -1 until proxy mode checks the server's MOTD on boot
Expand Down Expand Up @@ -165,6 +166,11 @@ def startServer(self):
self.backupInterval += 1
self.currentSecond = int(time.time())
self.wrapper.callEvent("timer.second", {})
if self.config["General"]["timed-reboot"]:
if time.time() - self.bootTime > self.config["General"]["timed-reboot-seconds"]:
self.stop("Server is conducting a scheduled reboot. The server will be back momentarily!")
self.start = True
self.bootTime = time.time()
if self.backupInterval == self.config["Backups"]["backup-interval"] and self.config["Backups"]["enabled"] and self.wrapper.callEvent("wrapper.backupBegin", {}):
self.backupInterval = 0
if not os.path.exists(self.config["Backups"]["backup-location"]):
Expand Down Expand Up @@ -288,6 +294,7 @@ def startServer(self):
self.msg("Server started")
self.log.info("Server started")
self.wrapper.callEvent("server.started", {})
self.bootTime = time.time()
elif self.argserver(3) == 'Starting' and self.argserver(4) == "minecraft":
self.version = self.argserver(7)
elif self.argserver(4) in deathPrefixes:
Expand Down
3 changes: 1 addition & 2 deletions src/storage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json, os, threading, time, copy
class Storage:
def __init__(self, name, isWorld=None, root=".wrapper-data/json"):
def __init__(self, name, isWorld=None, root="wrapper-data/json"):
self.name = name
self.root = root

Expand All @@ -14,7 +14,6 @@ def __init__(self, name, isWorld=None, root=".wrapper-data/json"):
t.daemon = True
t.start()
def __del__(self):
print "STORAGE OBJECT DESTROYED"
self.abort = True
self.save()
def __getitem__(self, index):
Expand Down

0 comments on commit 5abdf20

Please sign in to comment.