changeset 27:51428d30c59e ornery-orc

Traipse 'OpenRPG' {091003-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc''s main goal is to offer more advanced features and enhance the productivity of the user. Update Summary: Adds menu changes to draw attention to important updates, errors, or other events. Traipse URL is now included in the repos tab and is set as default. Fixes Copy for Windows and Linux (finally!!) users. Fixes incomplete update to Grid and List nodes. Fixes incomplete update to Chat Commands. Fixes problems with Remote Image Upload. Fixes Drop and Drag of Minis to Map. CherryPy can now use any image in the webfiles/ folder and sub-folders. CherryPy can now Drop and Drag Minis to the Map. Minor changes to Update Manager's GUI. Expert recommendation warning added to Revision Update. Step down compatibility with open_rpg & component added to orpgCore. Fixes CherryPit misspelling. Makes Traipse Suite 'Attention' item portable, and executes it on 'Critical' debug notices. Adds incomplete Shift + Enter to Text Entry, currently creates a 'Critical' warning. New default Lobby Map, designed for Traipse. Feel free to change it. Updates to Server GUI: * Admin can Ban from Backend. * Admin can Modify Ban List and Un Ban users. * Server GUI finds your Lobby Name * New users default as Lurker unless a Role is set Cleaner TraipseSuiteAttention portability and clean up in Main. Die Roll Commands addition from Core code allowing Math Ordering with ()'s New About Dialog. A more uniform About Dialog. Corrects image loading of duplicate images.
author sirebral
date Sat, 03 Oct 2009 18:49:37 -0500
parents 8b168989c344
children ff154cf3350c
files images/8ball.gif images/Copyright-Info.txt images/WAmisc7.ico images/WAmisc9.ico images/add_filter.gif images/apoc.gif images/arc.png images/b_d10.gif images/b_d100.gif images/b_d12.gif images/b_d20.gif images/b_d4.gif images/b_d6.gif images/b_d8.gif images/bold.gif images/book.gif images/bricktile.gif images/browser.gif images/bullet.gif images/car.gif images/ccmap.gif images/chess.gif images/circle.png images/clear.gif images/close_wnd.bmp images/compass.gif images/compass.ico images/connect.gif images/crosshair.gif images/cyborg.gif images/d10.gif images/d20.gif images/d20.ico images/d20.xpm images/d20_logo.gif images/d4.gif images/d8.gif images/dash.png images/defaultmap.png images/delete_filter.gif images/dice.bmp images/die.gif images/divider.png images/draw.gif images/drugs.gif images/earth.gif images/edit_filter.gif images/fetching.png images/flask.gif images/flask.ico images/fogoff.png images/fogon.png images/folder.gif images/form.png images/frame.bmp images/gear.gif images/goblin.gif images/goblin.ico images/grenade.gif images/grid.gif images/grid.ico images/gun1.gif images/gun2.gif images/help.gif images/hidefog.png images/html.gif images/html.ico images/icons.xml images/img.gif images/install.gif images/italic.gif images/knight.gif images/labtop.gif images/money.gif images/mouse.gif images/move.gif images/ninja.gif images/noplayer.gif images/note.gif images/note.ico images/open.bmp images/orc.gif images/oriental.gif images/pin.gif images/planet.gif images/player-whisper.gif images/player.gif images/python55.gif images/questionhead.gif images/r2d2.gif images/rectangle.png images/rome.gif images/save.bmp images/sflogo.png images/shades.gif images/showfog.png images/skull.gif images/skull_16.gif images/smsword2.gif images/spears.gif images/splash.gif images/splash.jpg images/splash1.jpg images/splash13.jpg images/splitwin.bmp images/spotlight.png images/startrek.gif images/sword.gif images/tab.bmp images/tab_close.png images/tab_on.png images/tabber.png images/tank1.gif images/tank2.gif images/tape.gif images/text.png images/thief.gif images/tiefighter.gif images/underlined.gif images/wizard1.gif images/wxPyButton.png images/wxWinButton.png images/zoom_in.gif images/zoom_out.gif myfiles/webfiles/Textures/Copyright Notice.txt myfiles/webfiles/Textures/grass-natural.jpg myfiles/webfiles/Textures/grass11.jpg myfiles/webfiles/Textures/sandwave.jpg myfiles/webfiles/Textures/steelpops11.jpg myfiles/webfiles/Textures/versa_anigre.jpg myfiles/webfiles/Textures/water06.jpg myfiles/webfiles/Textures/water20.jpg orpg/chat/chatwnd.py orpg/chat/commands.py orpg/dieroller/utils.py orpg/gametree/nodehandlers/core.py~ orpg/gametree/nodehandlers/forms.py orpg/gametree/nodehandlers/minilib.py orpg/gametree/nodehandlers/rpg_grid.py orpg/main.py orpg/mapper/background.py orpg/mapper/background_handler.py orpg/mapper/fog.py orpg/mapper/images.py orpg/mapper/miniatures.py orpg/mapper/miniatures_handler.py orpg/networking/mplay_client.py orpg/networking/mplay_server.py orpg/networking/mplay_server_gui.py orpg/orpgCore.py orpg/orpg_version.py orpg/templates/about.html orpg/templates/default_LobbyMessage.html orpg/templates/default_Lobby_map.xml orpg/templates/default_alias.alias orpg/templates/default_ban_list.xml orpg/templates/default_gui.xml orpg/templates/default_ignorelist.txt orpg/templates/default_ini.xml orpg/templates/default_layout.xml orpg/templates/default_manifest.xml orpg/templates/default_map.xml orpg/templates/default_plugindb.xml orpg/templates/default_server_ini.xml orpg/templates/default_settings.xml orpg/templates/default_tree.xml orpg/templates/feature.xml orpg/templates/metaservers.cache orpg/templates/nodes/Bastion_adventure.xml orpg/templates/nodes/Darwin_adventure.xml orpg/templates/nodes/FFE_adventure.xml orpg/templates/nodes/Idiots_guide_to_openrpg.xml orpg/templates/nodes/MiniatureLibrary.xml orpg/templates/nodes/StarWars_d20character.xml orpg/templates/nodes/Userguide098.xml orpg/templates/nodes/Userguide13.xml orpg/templates/nodes/adnd_2e_char_sheet.xml orpg/templates/nodes/alias.xml orpg/templates/nodes/browser.xml orpg/templates/nodes/d20character.xml orpg/templates/nodes/d20sites.xml orpg/templates/nodes/d20srd.xml orpg/templates/nodes/d20wizards.xml orpg/templates/nodes/default_map.xml orpg/templates/nodes/die_macro.xml orpg/templates/nodes/die_roller_notes.xml orpg/templates/nodes/dnd3.5.xml orpg/templates/nodes/dnd3e.xml orpg/templates/nodes/encounter.xml orpg/templates/nodes/form.xml orpg/templates/nodes/grid.xml orpg/templates/nodes/group.xml orpg/templates/nodes/image.xml orpg/templates/nodes/link.xml orpg/templates/nodes/listbox.xml orpg/templates/nodes/macro.xml orpg/templates/nodes/minlib.xml orpg/templates/nodes/openrpg_links.xml orpg/templates/nodes/split.xml orpg/templates/nodes/tabber.xml orpg/templates/nodes/text.xml orpg/templates/nodes/textctrl.xml orpg/templates/nodes/u_idiots_guide_to_openrpg.xml orpg/templates/nodes/urloader.xml orpg/templates/nodes/wizards.xml orpg/templates/templates.xml orpg/tools/aliaslib.py orpg/tools/orpg_log.py orpg/tools/orpg_settings.py orpg/tools/predTextCtrl.py upmana/default_manifest.xml upmana/updatemana.py
diffstat 27 files changed, 434 insertions(+), 194 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/images/Copyright-Info.txt	Sat Oct 03 18:49:37 2009 -0500
@@ -0,0 +1,2 @@
+Images by Sekkyumu and released for free use:
+    spotlight.png
Binary file images/spotlight.png has changed
--- a/orpg/chat/chatwnd.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/chat/chatwnd.py	Sat Oct 03 18:49:37 2009 -0500
@@ -72,7 +72,8 @@
 from orpg.tools.orpg_settings import settings
 from orpg.orpgCore import component
 from orpg.tools.orpg_log import logger
-from orpg.tools.decorators import debugging
+from orpg.tools.decorators import debugging
+
 NEWCHAT = False
 try:
     import wx.webview
@@ -175,11 +176,11 @@
         self.menu.AppendItem(item)
 
     @debugging
-    def OnM_EditCopy(self, evt):
-        wx.TheClipboard.Open()
-        wx.TheClipboard.Clear()
-        wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
-        wx.TheClipboard.Close()
+    def OnM_EditCopy(self, evt):
+        wx.TheClipboard.UsePrimarySelection(False)
+        wx.TheClipboard.Open()
+        wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
+        wx.TheClipboard.Close()
 
     @debugging
     def scroll_down(self):
@@ -289,8 +290,11 @@
             wx.CallAfter(self.parent.set_chat_text_focus, None)
 
         @debugging
-        def OnM_EditCopy(self, evt):
-            self.Copy()
+        def OnM_EditCopy(self, evt):
+            wx.TheClipboard.UsePrimarySelection(False)
+            wx.TheClipboard.Open()
+            wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
+            wx.TheClipboard.Close()
 
         #Cutom Methods
         @debugging
@@ -874,15 +878,10 @@
         self.Bind(wx.EVT_BUTTON, self.lock_scroll, self.scroll_lock)
         self.chattxt.Bind(wx.EVT_MOUSEWHEEL, self.chatwnd.mouse_wheel)
         self.chattxt.Bind(wx.EVT_CHAR, self.chattxt.OnChar)
-        self.chattxt.Bind(wx.EVT_TEXT_COPY, self.textCopy)
+        self.chattxt.Bind(wx.EVT_TEXT_COPY, self.chatwnd.OnM_EditCopy)
     # def build_ctrls - end
 
     @debugging
-    def textCopy(self, event):
-        if self.chattxt.GetStringSelection() == '': self.chatwnd.OnM_EditCopy(None)
-        else: self.chatwnd.Copy()
-
-    @debugging
     def build_bar(self):
         self.toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
         self.scroll_lock = None
@@ -1854,7 +1853,7 @@
         reg = re.compile("\[([^]]*?)\]")
         matches = reg.findall(s)
         for i in xrange(0,len(matches)):
-            newstr = self.PraseUnknowns(matches[i])
+            newstr = self.PraseUnknowns(matches[i])
             qmode = 0
             newstr1 = newstr
             if newstr[0].lower() == 'q':
@@ -1885,7 +1884,7 @@
             if newstr == '': newstr = '0'
             s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1)
             dlg.Destroy()
-        return s
+        return s
 
     # This subroutine builds a chat display name.
     #
@@ -1934,10 +1933,10 @@
 
     @debugging
     def resolve_loop(self, dom, nodeName, doLoop = False):
-        for node in dom:
+        for node in dom:
             if node._get_tagName() != 'nodehandler':
                 continue
-            if doLoop and node.getAttribute('class') != 'textctrl_handler' and node.hasChildNodes():
+            if doLoop and node.getAttribute('class') != 'textctrl_handler' and node.hasChildNodes():
                 (found, node) = self.resolve_loop(node.getChildren(), nodeName, doLoop)
                 if not found:
                     continue
--- a/orpg/chat/commands.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/chat/commands.py	Sat Oct 03 18:49:37 2009 -0500
@@ -14,6 +14,7 @@
 import orpg.orpg_windows
 import traceback
 
+from orpg.orpgCore import component
 from orpg.tools.orpg_log import logger
 from orpg.tools.decorators import debugging
 
@@ -262,7 +263,7 @@
     @debugging
     def on_dieroller(self, cmdargs):
         args = string.split(cmdargs,None,-1)
-        rm = self.chat.DiceManager
+        rm = component.get('DiceManager')
         try:
             rm.setRoller(args[0])
             self.chat.SystemPost("You have changed your die roller to the <b>\"" + args[0] + "\"</b> roller.")
--- a/orpg/dieroller/utils.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/dieroller/utils.py	Sat Oct 03 18:49:37 2009 -0500
@@ -82,7 +82,7 @@
     #  Use this to convert ndm-style (3d6) dice to d_base format
     
     def convertTheDieString(self,s):
-        reg = re.compile("\d+\s*[a-zA-Z]+\s*[\dFf]+")
+        reg = re.compile("(?:\d+|\([0-9\*/\-\+]+\))\s*[a-zA-Z]+\s*[\dFf]+")
         (result, num_matches) = reg.subn(self.stdDieToDClass, s)
         if num_matches == 0 or result is None:
             try:
--- a/orpg/gametree/nodehandlers/forms.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/gametree/nodehandlers/forms.py	Sat Oct 03 18:49:37 2009 -0500
@@ -28,7 +28,9 @@
 
 __version__ = "$Id: forms.py,v 1.53 2007/04/21 23:00:51 digitalxero Exp $"
 
-from containers import *
+from containers import *
+import orpg.minidom as minidom
+from orpg.orpg_xml import xml
 from wx.lib.scrolledpanel import ScrolledPanel
 
 def bool2int(b):
@@ -65,7 +67,7 @@
             else:
                 self.tree.load_xml(c,self.mytree_node)
         if not self.atts:
-            elem = self.xml.minidom.Element('form')
+            elem = minidom.Element('form')
             elem.setAttribute("width","400")
             elem.setAttribute("height","600")
             self.atts = self.master_dom.appendChild(elem)
@@ -285,7 +287,7 @@
 
     def on_text(self,evt):
         txt = self.text.GetValue()
-        txt = strip_text(txt)
+        txt = xml.strip_text(txt)
         self.handler.text._set_nodeValue(txt)
 
     def on_send(self,evt):
@@ -364,7 +366,7 @@
             self.handler.rename(txt)
         if id == F_TEXT:
             txt = self.text.GetValue()
-            txt = strip_text(txt)
+            txt = xml.strip_text(txt)
             self.handler.text._set_nodeValue(txt)
 
     def on_button(self,evt):
--- a/orpg/gametree/nodehandlers/minilib.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/gametree/nodehandlers/minilib.py	Sat Oct 03 18:49:37 2009 -0500
@@ -36,6 +36,7 @@
 import string
 import map_miniature_nodehandler
 import orpg.mapper.map_msg
+import orpg.minidom as minidom
 # import scriptkit
 
 # Constants
@@ -218,7 +219,7 @@
             label = mini.getAttribute( ATTRIBUTE_NAME )
         else:
             label = ''
-        return msg().get_all_xml()
+        return msg.get_all_xml()
 
     def is_unique( self, mini ):
         unique = mini.getAttribute( ATTRIBUTE_UNIQUE )
--- a/orpg/gametree/nodehandlers/rpg_grid.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/gametree/nodehandlers/rpg_grid.py	Sat Oct 03 18:49:37 2009 -0500
@@ -392,8 +392,7 @@
             t_node = cell.appendChild(t_node)
             r.appendChild(cell)
         self.AppendCols(1)
-        self.fit_cols()
-
+        #self.fit_cols()::Where did this go? TaS.
 
     def del_row(self,evt=None):
         num = self.GetNumberRows()
@@ -409,7 +408,7 @@
             cells = r.getElementsByTagName('cell')
             r.removeChild(cells[num-1])
         self.DeleteCols(num-1,1)
-        self.fit_cols()
+        #self.fit_cols()::Where did this go? TaS.
 
 
 G_TITLE = wx.NewId()
--- a/orpg/main.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/main.py	Sat Oct 03 18:49:37 2009 -0500
@@ -268,9 +268,30 @@
         self.Bind(wx.EVT_MENU, self.OnMB_UpdateManagerPanel, mana)
         self.traipseSuite.AppendItem(mana)
 
-        debugger = wx.MenuItem(self.traipseSuite, -1, "Debug Console", "Debug Console")
-        self.Bind(wx.EVT_MENU, self.OnMB_DebugConsole, debugger)
-        self.traipseSuite.AppendItem(debugger)
+        self.debugConsole = wx.MenuItem(self.traipseSuite, -1, "Debug Console", "Debug Console")
+        self.Bind(wx.EVT_MENU, self.OnMB_DebugConsole, self.debugConsole)
+        self.traipseSuite.AppendItem(self.debugConsole)
+
+    def TraipseSuiteWarn(self, menuitem):
+        ### Beta ### Allows for the reuse of the 'Attention' menu.
+        ### component.get('frame').TraipseSuiteWarn('item') ### Portable
+        self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite!')
+        if menuitem == 'debug':
+            if self.debugger.IsShown() == True:
+                self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite')
+            else:
+                self.debugConsole.SetBitmap(wx.Bitmap(dir_struct["icon"] + 'spotlight.png'))
+                self.traipseSuite.RemoveItem(self.debugConsole)
+                self.traipseSuite.AppendItem(self.debugConsole)
+
+    def TraipseSuiteWarnCleanup(self, menuitem):
+        ### Beta ### Allows for portable cleanup of the 'Attention' menu.
+        ### component.get('frame').TraipseSuiteWarnCleanup('item') ### Portable
+        self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite')
+        if menuitem == 'debug':
+            self.traipseSuite.RemoveItem(self.debugConsole)
+            self.debugConsole.SetBitmap(wx.Bitmap(dir_struct["icon"] + 'clear.gif'))
+            self.traipseSuite.AppendItem(self.debugConsole)
        
 
     #################################
@@ -474,6 +495,7 @@
 
     @debugging
     def OnMB_DebugConsole(self, evt):
+        self.TraipseSuiteWarnCleanup('debug') ### Beta ###
         if self.debugger.IsShown() == True: self.debugger.Hide()
         else: self.debugger.Show()
 
@@ -557,47 +579,18 @@
     #Help Menu #Needs a custom Dialog because it is ugly on Windows
     @debugging
     def OnMB_HelpAbout(self):
-
-        description = "OpenRPG is a Virtual Game Table that allows users to connect via a network and play table\n"
-        description += "top games with friends.  'Traipse' is an OpenRPG distro that is easy to setup and provides superb \n"
-        description += "functionality.  OpenRPG is originally designed by Chris Davis. \n"
-
-        license = "OpenRPG is free software; you can redistribute it and/or modify it "
-        license += "under the terms of the GNU General Public License as published by the Free Software Foundation; \n"
-        license += "either version 2 of the License, or (at your option) any later version.\n\n"
-        license += "OpenRPG and Traipse 'OpenRPG' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; \n"
-        license += "without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  \n"
-        license += "See the GNU General Public License for more details. You should have received a copy of \n"
-        license += "the GNU General Public License along with Traipse 'OpenRPG'; if not, write to \n"
-        license += "the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n"
-        license += "'Traipse' and the 'Traipse' Logo are trademarks of Mad Mathematics Laboratories."
-
-        info = wx.AboutDialogInfo()
-        info.SetIcon(wx.Icon(dir_struct["icon"]+'splash.gif', wx.BITMAP_TYPE_GIF))
-        info.SetName('Traipse')
-        info.SetVersion('OpenRPG ' + VERSION)
-        info.SetDescription(description)
-        info.SetCopyright('(C) Copyright 2009 Mad Math Labs')
-        info.SetWebSite('http://www.openrpg.com')
-        info.SetLicence(license)
-        orpg_devs = ['Thomas Baleno', 'Andrew Bennett', 'Lex Berezhny', 'Ted Berg',
-		'Bernhard Bergbauer', 'Chris Blocher', 'David Byron', 'Ben Collins-Sussman', 'Robin Cook', 'Greg Copeland',
-		'Chris Davis', 'Michael Edwards', 'Andrew Ettinger', 'Todd Faris', 'Dj Gilcrease',
-        'Christopher Hickman', 'Paul Hosking', 'Brian Manning', 'Scott Mackay', 'Jesse McConnell', 
-		'Brian Osman', 'Rome Reginelli', 'Christopher Rouse', 'Dave Sanders', 'Tyler Starke', 'Mark Tarrabain']
-        for dev in orpg_devs:
-            info.AddDeveloper(dev)
-        wx.AboutBox(info)
+        if self.AboutORPG.IsShown() == True: self.AboutORPG.Hide()
+        else: self.AboutORPG.Show()
 
     @debugging
     def OnMB_HelpOnlineUserGuide(self):
         wb = webbrowser.get()
-        wb.open("https://www.assembla.com/wiki/show/traipse/User_Manual")
+        wb.open("http://www.assembla.com/wiki/show/traipse/User_Manual")
 
     @debugging
     def OnMB_HelpChangeLog(self):
         wb = webbrowser.get()
-        wb.open("http://www.assembla.com/spaces/milestones/index/traipse_dev?spaces_tool_id=Milestones")
+        wb.open("http://www.assembla.com/spaces/milestones/index/traipse?spaces_tool_id=Milestones")
 
     @debugging
     def OnMB_HelpReportaBug(self):
@@ -629,6 +622,9 @@
         self.windowsmenu = wx.Menu()
         self.mainwindows = {}
 
+        # About Window
+        self.AboutORPG = AboutORPG(self)
+
         #Plugins Window
         self.pluginsFrame = pluginUI.PluginFrame(self)
         component.add("plugins", self.get_activeplugins())
@@ -642,7 +638,7 @@
         self.SetDimensions(posx, posy, w, h)
         logger.debug("Dimensions Set")
 
-        #Update Manager
+        # Update Manager
         self.manifest = manifest.ManifestChanges()
         self.updateMana = upmana.updatemana.updaterFrame(self, 
             "OpenRPG Update Manager Beta 0.8", component, self.manifest, True)
@@ -655,8 +651,7 @@
         self.SetDimensions(posx, posy, w, h)
         logger.debug("Dimensions Set")
 
-        #Update Manager
-        self.manifest = manifest.ManifestChanges()
+        # Debug Console
         self.debugger = orpg.tools.orpg_log.DebugConsole(self)
         logger.debug("Menu Created")
         h = int(xml_dom.getAttribute("height"))
@@ -732,6 +727,7 @@
         wndinfo.Name("Browse Server Window")
         wndinfo.Caption("Game Server")
         wndinfo.Float()
+        wndinfo.FloatingPosition((50,50))
         wndinfo.Dockable(False)
         wndinfo.MinSize(wx.Size(640,480))
         wndinfo.Hide()
@@ -1149,6 +1145,58 @@
         except Exception:
             pass
 
+########################################
+## About Dialog class
+########################################
+class AboutORPG(wx.Frame):
+    def __init__(self, parent):
+        super(AboutORPG, self).__init__(parent, -1, "About 'Traipse' OpenRPG")
+        icon = None
+        icon = wx.Icon(dir_struct["icon"]+'d20.ico', wx.BITMAP_TYPE_ICO)
+        self.SetIcon( icon )
+        sizer = wx.GridBagSizer(hgap=1, vgap=1)
+        height = 425; width = 350
+        self.About = wx.TextCtrl(self, -1, size=wx.DefaultSize, style=wx.TE_MULTILINE | wx.TE_READONLY)
+        img = wx.Image(dir_struct['icon']+'splash.gif', wx.BITMAP_TYPE_ANY).ConvertToBitmap()
+        image = wx.StaticBitmap(self, -1, img, size=wx.DefaultSize)
+        sizer.Add(image, (0,0), flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALL|wx.ADJUST_MINSIZE)
+        sizer.Add(self.About, (1,0), flag=wx.EXPAND)
+        self.SetSizer(sizer)
+        self.SetAutoLayout(True)
+        self.SetSize((width, height))
+        sizer.AddGrowableCol(0)
+        sizer.AddGrowableCol(1)
+        sizer.AddGrowableRow(1)
+        self.Bind(wx.EVT_CLOSE, self.Min) 
+        self.Min(None)
+
+        description = "OpenRPG is a Virtual Game Table that allows users to connect via a network and play table "
+        description += "top games with friends.  'Traipse' is an OpenRPG distro that is easy to setup and provides superb "
+        description += "functionality.  OpenRPG is originally designed by Chris Davis."
+
+        license = "OpenRPG is free software; you can redistribute it and/or modify it "
+        license += "under the terms of the GNU General Public License as published by the Free Software Foundation; "
+        license += "either version 2 of the License, or (at your option) any later version.\n\n"
+        license += "OpenRPG and Traipse 'OpenRPG' is distributed in the hope that it will be useful, but WITHOUT ANY "
+        license += "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "
+        license += "See the GNU General Public License for more details. You should have received a copy of "
+        license += "the GNU General Public License along with Traipse 'OpenRPG'; if not, write to "
+        license += "the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA \n\n"
+        license += "'Traipse' and the 'Traipse' Logo are trademarks of Mad Mathematics Laboratories."
+
+        self.About.AppendText(description+'\n\n')
+        self.About.AppendText(license+'\n\n')
+        self.About.AppendText('OpenRPG Developers:\n')
+        orpg_devs = ['Thomas Baleno', 'Andrew Bennett', 'Lex Berezhny', 'Ted Berg',
+		'Bernhard Bergbauer', 'Chris Blocher', 'David Byron', 'Ben Collins-Sussman', 'Robin Cook', 'Greg Copeland',
+		'Chris Davis', 'Michael Edwards', 'Andrew Ettinger', 'Todd Faris', 'Dj Gilcrease',
+        'Christopher Hickman', 'Paul Hosking', 'Brian Manning', 'Scott Mackay', 'Jesse McConnell', 
+		'Brian Osman', 'Rome Reginelli', 'Christopher Rouse', 'Dave Sanders', 'Tyler Starke', 'Mark Tarrabain']
+        for dev in orpg_devs:
+            self.About.AppendText(dev+'\n')
+
+    def Min(self, evt):
+        self.Hide()
 
 ########################################
 ## Application class
--- a/orpg/mapper/background.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/background.py	Sat Oct 03 18:49:37 2009 -0500
@@ -33,6 +33,7 @@
 import urllib
 import os.path
 import time
+import mimetypes
 
 from orpg.orpgCore import component
 from orpg.tools.orpg_log import logger
--- a/orpg/mapper/background_handler.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/background_handler.py	Sat Oct 03 18:49:37 2009 -0500
@@ -90,8 +90,8 @@
             else:
                 try: min_url = component.get("cherrypy") + filename
                 except: return
-                min_url = dlg.GetDirectory().replace(orpg.dirpath.dir_struct["user"]+'webfiles' + os.sep, 
-                    component.get("cherrypy")) + '/' + filename
+                min_url = dlg.GetDirectory().replace(orpg.dirpath.dir_struct["user"]+'webfiles', 
+                    component.get("cherrypy")).replace(os.sep, '/') + '/' + filename
 
                 if self.bg_type.GetStringSelection() == 'Texture': self.canvas.layers['bg'].set_texture(min_url)
                 elif self.bg_type.GetStringSelection() == 'Image': self.size = self.canvas.layers['bg'].set_image(min_url,1)
--- a/orpg/mapper/fog.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/fog.py	Sat Oct 03 18:49:37 2009 -0500
@@ -33,6 +33,7 @@
 
 class FogArea:
     def __init__(self, outline, log):
+        self.log = log
         self.outline = outline
 
     def set_fog_props(self, str):
@@ -79,6 +80,7 @@
 class fog_layer(layer_base):
     def __init__(self, canvas):
         self.canvas = canvas
+        self.log = component.get('log')
         layer_base.__init__(self)
         self.color = wx.Color(128,128,128)
         if "__WXGTK__" not in wx.PlatformInfo: self.color = wx.Color(128,128,128, 128)
--- a/orpg/mapper/images.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/images.py	Sat Oct 03 18:49:37 2009 -0500
@@ -78,12 +78,10 @@
                 self.__cache[path] = (path, d[0], d[1].gettype(), None)
                 return wx.ImageFromMime(self.__cache[path][1], self.__cache[path][2]).ConvertToBitmap()
             else:
-                logger.general("Image refused to load or URI did not reference a valid image: " + path)
-                component.get('chat').InfoPost("<font color='#FF0000'>Image refused to load or URI did not reference a valid image: " + path + "</font>")
+                logger.exception(str("Image refused to load or URI did not reference a valid image: " + path), True)
                 return None
         except IOError:
-            logger.general("Unable to resolve/open the specified URI; image was NOT loaded: " + path)
-            component.get('chat').InfoPost("<font color='#FF0000'>Unable to resolve/open the specified URI; image was NOT loaded: " + path + "</font>")
+            logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True)
             return None
 
     def cleanCache(self):
@@ -98,10 +96,11 @@
         """This function will flush all images contained within the image cache."""
         self.__lock.acquire()
         try:
-            keyList = self.__cache.keys()
-            for key in keyList: del self.__cache[key]
-        finally: self.__lock.release()
-        urllib.urlcleanup()
+            self.__cache = {}
+            self.__fetching = {}
+        finally: 
+            self.__lock.release()
+            urllib.urlcleanup()
 
     """Private Methods"""
     def __loadThread(self, path, image_type, imageId):
@@ -114,37 +113,32 @@
             if d[0] and d[1].getmaintype() == "image":
                 self.__cache[path] = (path, d[0], d[1].gettype(), imageId)
                 self.__queue.put((self.__cache[path], image_type, imageId))
-                if self.__fetching.has_key(path): self.__fetching[path] = False #Fix for failed multi-load?
+                if self.__fetching.has_key(path): del self.__fetching[path]
             else:
-                logger.general("Image refused to load or URI did not reference a valid image: " + path)
-                component.get('chat').InfoPost("<font color='#FF0000'>Image refused to load or URI did not reference a valid image: " + path +"</font>")
+                logger.exception(str("Image refused to load or URI did not reference a valid image: " + path), True)
                 del self.__fetching[path]
         except IOError:
             del self.__fetching[path]
-            logger.general("Unable to resolve/open the specified URI; image was NOT loaded: " + path)
-            component.get('chat').InfoPost("<font color='#FF0000'> Unable to resolve/open the specified URI; image was NOT loaded: " + path + "</font>")
+            logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True)
         finally: self.__lock.release()
 
     def __loadCacheThread(self, path, image_type, imageId):
-        if self.__cache.has_key(path):
-            try:
-                st = time.time()
-                while self.__fetching.has_key(path) and self.__fetching[path] is not False:
-                    time.sleep(0.025)
-                    if (time.time()-st) > 120:
-                        logger.general("Timeout: " + path)
-                        break
-            except:
-                del self.__fetching[path]
-                logger.general("Unable to resolve/open the specified URI; image was NOT loaded: " + path)
-                component.get('chat').InfoPost("<font color='#FF0000'>Unable to resolve/open the specified URI; image was NOT loaded: " + path + "</font>")
-                return 
-            self.__lock.acquire()
-            try:
-                logger.debug("Adding Image to Queue from Cache: " + str(self.__cache[path]))
-                component.debug('chat').InfoPost("<font color='#FF0000'>Adding Image to Queue from Cache: " + str(self.__cache[path]) + "</font>")
-                self.__queue.put((self.__cache[path], image_type, imageId))
-            finally: self.__lock.release()
+        try:
+            st = time.time()
+            while self.__fetching.has_key(path) and self.__fetching[path] is not False:
+                time.sleep(0.025)
+                if (time.time()-st) > 120:
+                    logger.general("Timeout: " + path)
+                    break
+        except:
+            del self.__fetching[path]
+            logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True)
+            return 
+        self.__lock.acquire()
+        try:
+            logger.info("Adding Image to Queue from Cache: " + str(self.__cache[path]), True)
+            self.__queue.put((self.__cache[path], image_type, imageId))
+        finally: self.__lock.release()
 
     """Property Methods"""
     def _getCache(self):
@@ -158,3 +152,4 @@
     Queue = property(_getQueue)
 
 ImageHandler = singleton(ImageHandlerClass)
+component.add('ImageHandler', ImageHandler)
--- a/orpg/mapper/miniatures.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/miniatures.py	Sat Oct 03 18:49:37 2009 -0500
@@ -33,7 +33,9 @@
 import time
 import urllib
 import os.path
+import mimetypes
 
+import xml.dom.minidom as minidom
 from orpg.tools.orpg_settings import settings
 
 MIN_STICKY_BACK = -0XFFFFFF
@@ -610,8 +612,9 @@
         except Exception, e:
             print e
             print recvdata
-        urllib.urlcleanup()
-        self.lock.release()
+        finally:
+            urllib.urlcleanup()
+            self.lock.release()
 ####################################################################
         ## helper function
 
--- a/orpg/mapper/miniatures_handler.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/mapper/miniatures_handler.py	Sat Oct 03 18:49:37 2009 -0500
@@ -33,7 +33,7 @@
 import time
 import mimetypes
 import urllib
-import xml.dom.minidom as minidom
+
 import wx
 from grid import GRID_RECTANGLE
 from grid import GRID_HEXAGON
@@ -167,38 +167,41 @@
             thread.start_new_thread(self.canvas.layers['miniatures'].upload, 
                                     (postdata, dlg.GetPath()), {'pos':cmpPoint(x,y)})
         else:
-            try: min_url = component.get("cherrypy") + filename
-            except: return #chat.InfoPost('CherryPy is not started!')
-            min_url = dlg.GetDirectory().replace(dir_struct["user"]+'webfiles' + os.sep, 
-                component.get("cherrypy")) + '/' + filename
-            # build url
-            if min_url == "" or min_url == "http://": return
-            if min_url[:7] != "http://": min_url = "http://" + min_url
-            # make label
-            if self.auto_label and min_url[-4:-3] == '.':
-                start = min_url.rfind("/") + 1
-                min_label = min_url[start:len(min_url)-4]
-                if self.use_serial: min_label = '%s %d' % ( min_label, self.canvas.layers['miniatures'].next_serial() )
-            else: min_label = ""
-            if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url)
-            try:
-                id = 'mini-' + self.canvas.frame.session.get_next_id()
-                # make the new mini appear in top left of current viewable map
-                dc = wx.ClientDC(self.canvas)
-                self.canvas.PrepareDC(dc)
-                dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
-                x = dc.DeviceToLogicalX(0)
-                y = dc.DeviceToLogicalY(0)
-                self.canvas.layers['miniatures'].add_miniature(id, min_url, pos=cmpPoint(x,y), label=min_label)
-            except:
-                # When there is an exception here, we should be decrementing the serial_number for reuse!!
-                unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n"
-                dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION)
-                dlg.ShowModal()
-                dlg.Destroy()
-                self.canvas.layers['miniatures'].rollback_serial()
-            self.canvas.send_map_data()
-            self.canvas.Refresh(False)
+            self.CherryPit(dlg.GetDirectory(), filename)
+
+    def CherryPit(self, path, filename):
+        try: min_url = component.get("cherrypy") + filename
+        except: return #chat.InfoPost('CherryPy is not started!')
+        min_url = path.replace(orpg.dirpath.dir_struct["user"]+'webfiles', 
+                component.get("cherrypy")).replace(os.sep, '/') + '/' + filename
+        # build url
+        if min_url == "" or min_url == "http://": return
+        if min_url[:7] != "http://": min_url = "http://" + min_url
+        # make label
+        if self.auto_label and min_url[-4:-3] == '.':
+            start = min_url.rfind("/") + 1
+            min_label = min_url[start:len(min_url)-4]
+            if self.use_serial: min_label = '%s %d' % ( min_label, self.canvas.layers['miniatures'].next_serial() )
+        else: min_label = ""
+        if self.min_url.FindString(min_url) == -1: self.min_url.Append(min_url)
+        try:
+            id = 'mini-' + self.canvas.frame.session.get_next_id()
+            # make the new mini appear in top left of current viewable map
+            dc = wx.ClientDC(self.canvas)
+            self.canvas.PrepareDC(dc)
+            dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale)
+            x = dc.DeviceToLogicalX(0)
+            y = dc.DeviceToLogicalY(0)
+            self.canvas.layers['miniatures'].add_miniature(id, min_url, pos=cmpPoint(x,y), label=min_label)
+        except:
+            # When there is an exception here, we should be decrementing the serial_number for reuse!!
+            unablemsg= "Unable to load/resolve URL: " + min_url + " on resource \"" + min_label + "\"!!!\n\n"
+            dlg = wx.MessageDialog(self,unablemsg, 'Url not found',wx.ICON_EXCLAMATION)
+            dlg.ShowModal()
+            dlg.Destroy()
+            self.canvas.layers['miniatures'].rollback_serial()
+        self.canvas.send_map_data()
+        self.canvas.Refresh(False)
 
 
     def build_menu(self,label = "Miniature"):
@@ -614,7 +617,10 @@
         y = dc.DeviceToLogicalY(y)
         (imgtype,j) = mimetypes.guess_type(filename)
         postdata = urllib.urlencode({'filename':filename, 'imgdata':imgdata, 'imgtype':imgtype})
-        thread.start_new_thread(self.canvas.layers['miniatures'].upload, (postdata, filepath), {'pos':cmpPoint(x,y)})
+        if self.settings.get_setting('LocalorRemote') == 'Remote':
+            thread.start_new_thread(self.canvas.layers['miniatures'].upload, (postdata, filepath), {'pos':cmpPoint(x,y)})
+        else:
+            self.CherryPit(filepath, '')
 
     def on_tooltip_timer(self, *args):
         pos = args[0]
--- a/orpg/networking/mplay_client.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/networking/mplay_client.py	Sat Oct 03 18:49:37 2009 -0500
@@ -29,7 +29,8 @@
 
 __version__ = "$Id: mplay_client.py,v 1.71 2007/05/12 20:41:54 digitalxero Exp $"
 
-#import orpg.minidom
+### Alpha ### 
+##import orpg.minidom ## Deprecated. xml.parseXml calls minidom.parseString so it was superfluous and wasteful.
 import socket
 import Queue
 import thread
--- a/orpg/networking/mplay_server.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/networking/mplay_server.py	Sat Oct 03 18:49:37 2009 -0500
@@ -2080,8 +2080,22 @@
             traceback.print_exc()
             self.log_msg('Exception in admin_kick() ' + str(e))
 
+    ### Alpha ### Addition added to assist in Un Banning users.
+    def admin_build_banlist(self):
+        validate.config_file("ban_list.xml", "default_ban_list.xml" ) 
+        configDom = minidom.parse(dir_struct["user"] + 'ban_list.xml')
+        self.ban_list = {}
+        for element in configDom.getElementsByTagName('banned'):
+            player = element.getAttribute('name').replace("&", "&amp;").replace("<", "&lt;").replace('"', "&quot;").replace(">", "&gt;")
+            ip = element.getAttribute('ip')
+            self.ban_list[ip] = {}
+            self.ban_list[ip]['ip'] = ip
+            self.ban_list[ip]['name'] = element.getAttribute('name')
+    ################
+
     def admin_banip(self, ip, name="", silent = 0):
         "Ban a player from a server from the console"
+        self.adming_buile_banlist() ### Alpha ###
         try:
             self.ban_list[ip] = {}
             self.ban_list[ip]['ip'] = ip
@@ -2125,6 +2139,7 @@
             self.log_msg('Exception in admin_ban() ' + str(e))
 
     def admin_unban(self, ip):
+        self.admin_build_banlist()
         try:
             if self.ban_list.has_key(ip): del self.ban_list[ip]
             self.saveBanList()
@@ -2571,3 +2586,5 @@
         except Exception, e: self.log_msg(str(e))
         self.p_lock.release()
         return pl
+
+server = mplay_server()
--- a/orpg/networking/mplay_server_gui.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/networking/mplay_server_gui.py	Sat Oct 03 18:49:37 2009 -0500
@@ -15,13 +15,14 @@
 import types
 from orpg.dirpath import dir_struct
 #import orpg.systempath looks old
-from orpg.tools.validate import Validate
+from orpg.tools.validate import validate
 from orpg.orpg_wx import *
 import webbrowser
 from threading import Thread
 from meta_server_lib import post_server_data, remove_server
-from mplay_server import mplay_server
+from mplay_server import mplay_server, server
 from xml.dom import minidom
+from orpg.orpgCore import component
 
 # Constants ######################################
 SERVER_RUNNING = 1
@@ -34,9 +35,17 @@
 MENU_START_PING_PLAYERS = wx.NewId()
 MENU_STOP_PING_PLAYERS = wx.NewId()
 MENU_PING_INTERVAL = wx.NewId()
+### Alpha ###
+MENU_MODIFY_BANLIST = wx.NewId()
+#############
 
 # Add our menu id's for our right click popup
 MENU_PLAYER_BOOT = wx.NewId()
+### Alpha ###
+MENU_ADMIN_BAN = wx.NewId()
+MENU_BAN_LIST = wx.NewId()
+MENU_ADMIN_UNBAN = wx.NewId()
+#############
 MENU_PLAYER_CREATE_ROOM = wx.NewId()
 MENU_PLAYER_SEND_MESSAGE = wx.NewId()
 MENU_PLAYER_SEND_ROOM_MESSAGE = wx.NewId()
@@ -83,12 +92,11 @@
 # ServerConfig Object ############################
 class ServerConfig:
     """ This class contains configuration
-        setting used to control the server.
-    """
+        setting used to control the server."""
 
     def __init__(self, owner ): 
         """ Loads default configuration settings."""
-        Validate(dir_struct["user"]).config_file("server_ini.xml", "default_server_ini.xml" ) 
+        validate.config_file("server_ini.xml", "default_server_ini.xml" ) 
         configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') 
         port = configDom.childNodes[0].childNodes[1].getAttribute('port')
         OPENRPG_PORT = 6774 if port == '' else int(port) #Pretty ugly, but I couldn't find the tag any other way.
@@ -96,14 +104,12 @@
 
     def load_xml(self, xml):
         """ Load configuration from XML data.
-            xml (xml) -- xml string to parse
-        """
+            xml (xml) -- xml string to parse """
         pass
 
     def save_xml(self):
         """ Returns XML file representing
-            the active configuration.
-        """
+            the active configuration. """
         pass
 
 # Server Monitor #################################
@@ -146,7 +152,14 @@
         wx.ListCtrl.__init__( self, parent, -1, wx.DefaultPosition, 
                             wx.DefaultSize, wx.LC_REPORT|wx.SUNKEN_BORDER|wx.EXPAND|wx.LC_HRULES )
         self.main = main
-        self.roomList = { 0 : "Lobby" }
+
+        ### Alpha ### Get Lobby Name
+        validate.config_file("server_ini.xml", "default_server_ini.xml" ) 
+        configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') 
+        lobbyname = configDom.childNodes[0].getAttribute('lobbyname')
+        #############
+
+        self.roomList = { 0 : lobbyname }
         self._imageList = wx.ImageList( 16, 16, False )
         img = wx.Image(dir_struct["icon"]+"player.gif", wx.BITMAP_TYPE_GIF).ConvertToBitmap()
         self._imageList.Add( img )
@@ -171,6 +184,7 @@
         self.menu = wx.Menu()
         self.menu.SetTitle( "Player Menu" )
         self.menu.Append( MENU_PLAYER_BOOT, "Boot Player" )
+        self.menu.Append( MENU_ADMIN_BAN, 'Ban Player' )
         self.menu.AppendSeparator()
         self.menu.Append( MENU_PLAYER_SEND_MESSAGE, "Send Player Message" )
         self.menu.Append( MENU_PLAYER_SEND_ROOM_MESSAGE, "Send Room Message" ) 
@@ -179,6 +193,7 @@
         # Associate our events
         self.Bind(wx.EVT_RIGHT_DOWN, self.OnPopupMenu)
         self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_BOOT)
+        self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_ADMIN_BAN)
         self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_MESSAGE)
         self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_ROOM_MESSAGE)
         self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_SERVER_MESSAGE)
@@ -186,10 +201,10 @@
     def add(self, player):
         i = self.InsertImageStringItem( 0, player["id"], 0 )
         self.SetStringItem( i, 1, self.stripHtml( player["name"] ) )
-        self.SetStringItem( i, 2, "new" )
+        self.SetStringItem( i, 2, "NEW" )
         self.SetStringItem( i, 3, self.roomList[0] )
         self.SetStringItem( i, 4, self.stripHtml( player["version"] ) )
-        self.SetStringItem( i, 5, self.stripHtml( player["role"] ) )
+        self.SetStringItem( i, 5, 'Lurker' if self.stripHtml( player["role"] ) == '' else self.stripHtml( player["role"] ))
         self.SetStringItem( i, 6, self.stripHtml( player["ip"] ) )
         self.SetStringItem (i, 7, "PING" )
         self.SetItemData( i, int(player["id"]) )
@@ -216,18 +231,20 @@
         if i > -1:
             self.SetStringItem(i, 1, self.stripHtml(player["name"]))
             self.SetStringItem(i, 2, self.stripHtml(player["status"]))
+            self.SetStringItem(i, 5, 'Lurker' if self.stripHtml(player["role"]) == '' else self.stripHtml(player["role"]))
             self.AutoAjust()
         else: self.add(player)
 
     def updateRoom( self, data ):
         (room, room_id, player) = data
         i = self.FindItemData( -1, int(player) )
-        if i > 0: self.SetStringItem( i, 3, room )
+        if player > 0: self.SetStringItem( i, 3, room )
         self.AutoAjust()
 
     def setPlayerRole( self, id, role ):
         i = self.FindItemData( -1, int(id) )
         self.SetStringItem( i, 5, role )
+        self.AutoAjust
 
     def stripHtml( self, name ):
         ret_string = ""
@@ -265,6 +282,20 @@
                 self.main.server.server.del_player( playerID, groupID )
                 self.main.server.server.check_group( playerID, groupID )
                 self.remove( playerID )
+            ### Alpha ###
+            elif menuItem == MENU_ADMIN_BAN:
+                message = 'Banishment'
+                BanMsg = wx.TextEntryDialog( self, "Enter A Message To Send:",
+                                                 "Ban Message", message, wx.OK|wx.CANCEL|wx.CENTRE )
+                if BanMsg.ShowModal() == wx.ID_OK: message = BanMsg.GetValue()
+                else: message = ''
+                Silent = wx.MessageDialog(None, 'Silent Ban?', 'Question', 
+                    wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+                if Silent.ShowModal() == wx.ID_YES: silent = 1
+                else: silent = 0
+                self.main.server.server.admin_ban(playerID, message, silent)
+                self.remove( playerID )
+            ###############
             elif menuItem == MENU_PLAYER_SEND_MESSAGE:
                 print "send a message..."
                 msg = self.GetMessageInput( "Send a message to player" )
@@ -307,6 +338,11 @@
         self.build_body()
         self.build_status()
 
+        ### Alpha ###
+        # Ban List Dialog
+        self.BanListDialog = BanListDialog(self)
+        #############
+
         # Server Callbacks
         cb = {}
         cb["log"]        = self.Log
@@ -355,6 +391,8 @@
 
         # Server Configuration Menu
         menu = wx.Menu()
+        menu.Append( MENU_BAN_LIST, 'Ban List', 'Modify Ban List.' )
+        self.Bind(wx.EVT_MENU, self.ModifyBanList, id=MENU_BAN_LIST)
         menu.Append( MENU_START_PING_PLAYERS, 'Start Ping', 'Ping players to validate remote connection.' )
         self.Bind(wx.EVT_MENU, self.PingPlayers, id=MENU_START_PING_PLAYERS)
         menu.Append( MENU_STOP_PING_PLAYERS, 'Stop Ping', 'Stop validating player connections.' )
@@ -470,19 +508,20 @@
         if self.STATUS == SERVER_STOPPED:
             # see if we already have name specified 
             try:
-                Validate(dir_struct["user"]).config_file( "server_ini.xml", "default_server_ini.xml" ) 
+                validate.config_file( "server_ini.xml", "default_server_ini.xml" ) 
                 configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') 
                 configDom.normalize() 
                 configDoc = configDom.documentElement 
                 if configDoc.hasAttribute("name"): self.serverName = configDoc.getAttribute("name")
             except: pass 
-            if self.serverName == '': 
+            if self.serverName == '':
+                self.serverName = 'Server Name'
                 serverNameEntry = wx.TextEntryDialog( self, "Please Enter The Server Name You Wish To Use:",
                                                  "Server's Name", self.serverName, wx.OK|wx.CANCEL|wx.CENTRE )
                 if serverNameEntry.ShowModal() == wx.ID_OK: self.serverName = serverNameEntry.GetValue()
             # see if we already have password specified 
             try: 
-                Validate(dir_struct["user"]).config_file( "server_ini.xml", "default_server_ini.xml" ) 
+                validate.config_file( "server_ini.xml", "default_server_ini.xml" ) 
                 configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') 
                 configDom.normalize() 
                 configDoc = configDom.documentElement 
@@ -551,6 +590,12 @@
         self.SetTitle(__appname__ + "- (running) - (unregistered)")
         wx.EndBusyCursor()
 
+    ### Alpha ###
+    def ModifyBanList(self, event):
+        if self.BanListDialog.IsShown() == True: self.BanListDialog.Hide()
+        else: self.BanListDialog.Show()
+    #############
+
     def PingPlayers( self, event = None ):
         "Ping all players that are connected at a periodic interval, detecting dropped connections."
         wx.BeginBusyCursor()
@@ -566,8 +611,79 @@
     def OnExit(self, event = None):
         """ Quit the program. """
         self.OnStop()
+        self.BanListDialog.Destroy() ### Alpha ###
         wx.CallAfter(self.Destroy)
 
+### Alpha ###
+class BanListDialog(wx.Frame):
+    def __init__(self, parent):
+        super(BanListDialog, self).__init__(parent, -1, "Ban List")
+        icon = wx.Icon(dir_struct["icon"]+'noplayer.gif', wx.BITMAP_TYPE_GIF)
+        self.SetIcon( icon )
+        self.BanList = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_SINGLE_SEL|wx.LC_REPORT|wx.LC_HRULES)
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.BanList, 1, wx.EXPAND)
+        self.BuildList()
+        self.SetSizer(sizer)
+        self.SetAutoLayout(True)
+        self.SetSize((300, 175))
+        self.Bind(wx.EVT_CLOSE, self.Min) 
+        self.Min(None)
+
+        # Ban List Dialog Pop Up Menu, more can be added
+        self.menu = wx.Menu()
+        self.menu.SetTitle( "Modify Ban List" )
+        self.menu.Append( MENU_ADMIN_UNBAN, "Un-Ban Player" )
+
+        # Even Association
+        self.BanList.Bind(wx.EVT_RIGHT_DOWN, self.BanPopupMenu)
+        self.Bind(wx.EVT_MENU, self.BanPopupMenuItem, id=MENU_ADMIN_UNBAN)
+
+    # When we right click, cause our popup menu to appear
+    def BanPopupMenu( self, event ):
+        pos = wx.Point( event.GetX(), event.GetY() )
+        (item, flag) = self.BanList.HitTest( pos )
+        if item > -1:
+            self.selectedItem = item
+            self.PopupMenu( self.menu, pos )
+
+    def BanPopupMenuItem( self, event):
+        menuItem = event.GetId()
+        player = str(self.BanList.GetItemData(self.selectedItem))
+        playerIP = str(self.BanList.GetItem((int(player)), 1).GetText())
+        if menuItem == MENU_ADMIN_UNBAN:
+            server.admin_unban(playerIP)
+            self.BanList.DeleteItem(self.BanList.GetItemData(self.selectedItem))
+            self.BanList.Refresh()   
+
+    def BuildList(self):
+        # Build Dialog Columns
+        self.BanList.ClearAll()
+        self.BanList.InsertColumn(0, "User Name")
+        self.BanList.InsertColumn(1, "IP")
+
+        validate.config_file("ban_list.xml", "default_ban_list.xml" ) 
+        configDom = minidom.parse(dir_struct["user"] + 'ban_list.xml')
+        ban_dict = {}
+        for element in configDom.getElementsByTagName('banned'):
+            player = element.getAttribute('name').replace("&", "&amp;").replace("<", "&lt;").replace('"', "&quot;").replace(">", "&gt;")
+            playerIP = element.getAttribute('ip')
+            ban_dict[player] = playerIP
+        for key in ban_dict:
+            i = self.BanList.InsertImageStringItem( 0, key, 0 )
+            self.BanList.SetStringItem(i, 1, ban_dict[key])
+            self.BanList.RefreshItem(i)
+        self.AutoAdjust()
+
+    def AutoAdjust(self):
+        self.BanList.SetColumnWidth(0, -1)
+        self.BanList.SetColumnWidth(1, -1)
+        self.BanList.Refresh()
+
+    def Min(self, evt):
+        self.Hide()
+###############
+
 class ServerGUIApp(wx.App):
     def OnInit(self):
         # Make sure our image handlers are loaded before we try to display anything
--- a/orpg/orpgCore.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/orpgCore.py	Sat Oct 03 18:49:37 2009 -0500
@@ -71,6 +71,13 @@
         if self.__components.has_key(key): del self.__components[key]
         else: return
 
+    ###Grumpy to Ornery###
+    def add_component(self, key, com):
+        return self.add(key, com)
+
+    def get_component(self, key):
+        return self.get(key)
+
 def singleton(cls):
     instances = {}
     def getinstance():
@@ -81,3 +88,6 @@
 
 ORPGStorage = singleton(ORPGStorage)
 component = ORPGStorage()
+
+###Grumpy to Ornery###
+open_rpg = ORPGStorage()
--- a/orpg/orpg_version.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/orpg_version.py	Sat Oct 03 18:49:37 2009 -0500
@@ -4,7 +4,7 @@
 #BUILD NUMBER FORMAT: "YYMMDD-##" where ## is the incremental daily build index (if needed)
 DISTRO = "Traipse"
 DIS_VER = "Ornery Orc"
-BUILD = "090827-04"
+BUILD = "091003-00"
 
 # This version is for network capability.
 PROTOCOL_VERSION = "1.2"
--- a/orpg/templates/default_Lobby_map.xml	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/templates/default_Lobby_map.xml	Sat Oct 03 18:49:37 2009 -0500
@@ -1,7 +1,7 @@
 <nodehandler class="min_map" icon="compass" module="core" name="miniature Map">
-<map version='1.0' sizex='300' sizey='300' action='new'>
+<map version='1.0' sizex='650' sizey='384' action='new'>
 <grid size='50'  mode='0' line='0' snap='1' color='#000000'/>
-<bg path='http://www.openrpg.com/images/maps/Lobby_image.png' type='2'/>
+<bg path='http://www.knowledgearcana.com/arcane-art/Traipse_Skeleton.png' type='2'/>
 <miniatures serial='2'/>
 </map>
 </nodehandler>
--- a/orpg/tools/aliaslib.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/tools/aliaslib.py	Sat Oct 03 18:49:37 2009 -0500
@@ -325,15 +325,16 @@
             defaultcolor = settings.get_setting("mytextcolor")
             settings.set_setting("mytextcolor", self.alias[1])
             self.chat.set_colors()
-        opts = []
+        opts = []; idlist = []
         myid = self.session.get_id()
         for p in players:
             if p[2] != myid: opts.append("(" + p[2] + ") " + self.chat.html_strip(p[0]))
+            if p[2] != myid: idlist.append(p[2])
         dlg = orpgMultiCheckBoxDlg(self, opts, "Select Players:", "Whisper To", [])
         sendto = []
         if dlg.ShowModal() == wx.ID_OK:
             selections = dlg.get_selections()
-            for s in selections: sendto.append(players[s][2])
+            for s in selections: sendto.append(idlist[s])
         line = self.textWnd.GetValue().replace("\n", "<br />")
         if self.checkFilterText.IsChecked() and self.filter != self.chat.defaultFilterName:
             for rule in self.filterRegEx: line = re.sub(rule[0], rule[1], line)
--- a/orpg/tools/orpg_log.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/tools/orpg_log.py	Sat Oct 03 18:49:37 2009 -0500
@@ -54,11 +54,10 @@
     crash_report.close()
     logger.exception("Crash Report Created!!")
     logger.info("Printed out crash-report.txt in your System folder", True)
-    wx.MessageBox('Crash Report Created!', 'System Failure')
 
 class DebugConsole(wx.Frame):
     def __init__(self, parent):
-        super(DebugConsole, self).__init__(parent, -1, "Debug Window")
+        super(DebugConsole, self).__init__(parent, -1, "Debug Console")
         icon = None
         icon = wx.Icon(dir_struct["icon"]+'note.ico', wx.BITMAP_TYPE_ICO)
         self.SetIcon( icon )
@@ -117,6 +116,8 @@
         self.log(msg, ORPG_GENERAL, to_console)
 
     def exception(self, msg, to_console=True):
+        ### Beta ### Every 'Critical' exception will draw attention to the Debug Console
+        component.get('frame').TraipseSuiteWarn('debug')
         self.log(msg, ORPG_CRITICAL, to_console)
 
     def log(self, msg, log_type, to_console=False):
--- a/orpg/tools/orpg_settings.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/tools/orpg_settings.py	Sat Oct 03 18:49:37 2009 -0500
@@ -33,41 +33,47 @@
 import sys
 import os
 from orpg.orpg_xml import xml
+from orpg.tools.orpg_log import logger
+from orpg.tools.validate import validate
+from orpg.orpg_xml import xml
 
 class orpgSettings:
     def __init__(self):
-        self.validate = component.get("validate")
-        component.add('xml', xml)
         self.xml = component.get("xml")
-        self.orpgLog = component.get("log")
         self.changes = []
-        self.validate.config_file("settings.xml","default_settings.xml")
+        validate.config_file("settings.xml","default_settings.xml")
         self.filename = dir_struct["user"] + "settings.xml"
         temp_file = open(self.filename)
         txt = temp_file.read()
         temp_file.close()
 
-        self.xml_dom = self.xml.parseXml(txt)
+        self.xml_dom = xml.parseXml(txt)
 
         if self.xml_dom is None: self.rebuildSettings()
         self.xml_dom = self.xml_dom._get_documentElement()
 
     def rebuildSettings(self):
-        self.orpgLog.log("Settings file has be corrupted, rebuilding settings.", ORPG_INFO, True)
+        logger.info("Settings file has be corrupted, rebuilding settings.", True)
         try: os.remove(self.filename)
         except: pass
 
-        self.validate.config_file("settings.xml","default_settings.xml")
+        validate.config_file("settings.xml","default_settings.xml")
         temp_file = open(self.filename)
         txt = temp_file.read()
         temp_file.close()
-        self.xml_dom = self.xml.parseXml(txt)
+        self.xml_dom = xml.parseXml(txt)
 
-    def get_setting(self, name):
+    def get_setting(self, name): ##Depricated
+        return self.get(name)
+
+    def get(self, name): 
         try: return self.xml_dom.getElementsByTagName(name)[0].getAttribute("value")
         except: return 0
 
-    def get_setting_keys(self):
+    def get_setting_keys(self): ##Depricated
+        return self.get_keys()
+
+    def get_keys(self):
         keys = []
         tabs = self.xml_dom.getElementsByTagName("tab")
         for i in xrange(0, len(tabs)):
@@ -76,13 +82,19 @@
                 for c in children: keys.append(c._get_tagName())
         return keys
 
-    def set_setting(self, name, value):
+    def set_setting(self, name, value): ##Depricated
+        self.change(name, value)
+
+    def change(self, name, value):
         self.xml_dom.getElementsByTagName(name)[0].setAttribute("value", value)
 
-    def add_setting(self, tab, setting, value, options, help):
+    def add_setting(self, tab, setting, value, options, help): ##Depricated
+        return self.add(tab, setting, value, options, help)
+
+    def add(self, tab, setting, value, options, help):
         if len(self.xml_dom.getElementsByTagName(setting)) > 0: return False
         tabs = self.xml_dom.getElementsByTagName("tab")
-        newsetting = self.xml.parseXml('<' + setting + ' value="' + value + '" options="' + 
+        newsetting = xml.parseXml('<' + setting + ' value="' + value + '" options="' + 
                                         options + '" help="' + help + '" />')._get_documentElement()
         for i in xrange(0, len(tabs)):
             if tabs[i].getAttribute("name") == tab and tabs[i].getAttribute("type") == 'grid':
@@ -94,7 +106,7 @@
         tab_xml = '<tab '
         if tabtype == 'text': tab_xml += 'name="' + tabname + '" type="text" />'
         else: tab_xml += 'name="' + tabname + '" type="' + tabtype + '"></tab>'
-        newtab = self.xml.parseXml(tab_xml)._get_documentElement()
+        newtab = xml.parseXml(tab_xml)._get_documentElement()
         if parent != None:
             tabs = self.xml_dom.getElementsByTagName("tab")
             for i in xrange(0, len(tabs)):
@@ -117,7 +129,7 @@
         temp_file = open(defaultFile)
         txt = temp_file.read()
         temp_file.close()
-        default_dom = self.xml.parseXml(txt)._get_documentElement()
+        default_dom = xml.parseXml(txt)._get_documentElement()
         for child in default_dom.getChildren():
             if child._get_tagName() == 'tab' and child.hasChildNodes(): self.proccessChildren(child)
         default_dom.unlink()
@@ -136,7 +148,7 @@
 
     def save(self):
         temp_file = open(self.filename, "w")
-        temp_file.write(self.xml.toxml(self.xml_dom,1))
+        temp_file.write(xml.toxml(self.xml_dom,1))
         temp_file.close()
 
 class orpgSettingsWnd(wx.Dialog):
@@ -145,7 +157,6 @@
                             wx.DefaultPosition,size = wx.Size(-1,-1), 
                             style=wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION)
         self.Freeze()
-        self.validate = component.get("validate")
         self.settings = component.get("settings")
         self.chat = component.get("chat")
         self.changes = []
@@ -172,7 +183,7 @@
         self.winsizer.SetDimension(0,0,w,h-25)
 
     def build_gui(self):
-        self.validate.config_file("settings.xml","default_settings.xml")
+        validate.config_file("settings.xml","default_settings.xml")
         filename = dir_struct["user"] + "settings.xml"
         temp_file = open(filename)
         temp_file.close()
--- a/orpg/tools/predTextCtrl.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/orpg/tools/predTextCtrl.py	Sat Oct 03 18:49:37 2009 -0500
@@ -34,6 +34,7 @@
 import string
 from orpg.orpg_windows import *
 from wx.lib.expando import ExpandoTextCtrl
+from orpg.tools.orpg_log import logger
 
 #  This line added to test CVS commit
 
@@ -434,6 +435,12 @@
                                                            #  clobber the prediction.
 
                     return                                 #  Don't pass tab on in this case
+            elif event.GetKeyCode() == wx.WXK_RETURN and event.ShiftDown():
+                logger.exception('Shift + Enter Not completed, 439, predtextCtrl', True)
+                st = self.GetValue()
+                st += '<br />'
+                return
+
 
             elif event.GetKeyCode() == wx.WXK_RETURN:            #  We want to hook returns, so that we can update the word list
 
--- a/upmana/default_manifest.xml	Thu Aug 27 01:28:11 2009 -0500
+++ b/upmana/default_manifest.xml	Sat Oct 03 18:49:37 2009 -0500
@@ -1,1 +1,13 @@
-<manifest></manifest>
+<manifest>
+	<updaterepo>
+		<repo-Traipse type="string">http://hg.assembla.com/traipse</repo-Traipse>
+		<default type="string">http://hg.assembla.com/traipse</default>
+	</updaterepo>
+	<UpdateManifest>
+		<repolist type="list">
+			<list>
+				<lobject type="str">repo-Traipse</lobject>
+			</list>
+		</repolist>
+	</UpdateManifest>
+</manifest>
--- a/upmana/updatemana.py	Thu Aug 27 01:28:11 2009 -0500
+++ b/upmana/updatemana.py	Sat Oct 03 18:49:37 2009 -0500
@@ -26,7 +26,7 @@
         self.parent = parent
         self.SetBackgroundColour(wx.WHITE)
         self.sizer = wx.GridBagSizer(hgap=1, vgap=1)
-        self.changelog = wx.TextCtrl(self, wx.ID_ANY, size=(325, -1), style=wx.TE_MULTILINE | wx.TE_READONLY)
+        self.changelog = wx.TextCtrl(self, wx.ID_ANY, size=(300, -1), style=wx.TE_MULTILINE | wx.TE_READONLY)
         self.filelist = wx.TextCtrl(self, wx.ID_ANY, size=(275, 300), style=wx.TE_MULTILINE | wx.TE_READONLY)
         self.buttons = {}
         self.buttons['progress_bar'] = wx.Gauge(self, wx.ID_ANY, 100)
@@ -436,8 +436,10 @@
 
         ## Branches / Revisions
         branchcp = wx.Panel(self)
+        self.current = self.repo.dirstate.branch()
         self.branchcp = wx.GridBagSizer(hgap=1, vgap=1)
         self.branches = wx.Choice(branchcp, wx.ID_ANY, choices=self.package_list)
+        self.branches.SetSelection(self.package_list.index(self.current))
         self.branch_txt = wx.StaticText(branchcp, wx.ID_ANY, "Branches")
         self.branchcp.Add(self.branches, (0,0))
         self.branchcp.Add(self.branch_txt, (0,1), flag=wx.ALIGN_CENTER_VERTICAL)
@@ -482,7 +484,6 @@
         self.SetSizer(self.sizer)
         self.SetAutoLayout(True)
 
-        self.current = self.repo.dirstate.branch()
         self.currev = self.repo.changelog.rev(self.repo.branchtags()[self.current])
         self.RevInfo(self.currev)
         self.revlist.Select(self.revlist.FindItem(0, str(self.currev), 1))
@@ -541,18 +542,22 @@
         pass
 
     def RevUpdate(self, event):
-        filename = 'ignorelist.txt'
-        self.filename = dir_struct["home"] + 'upmana' + os.sep + filename
-        component.get('validate').config_file(filename, "default_ignorelist.txt")
-        self.mana = self.LoadDoc()
-        temp = dir_struct["home"] + 'upmana' + os.sep + 'tmp' + os.sep
-        for ignore in self.ignorelist:
-            shutil.copy(ignore, temp + ignore.split('/')[len(ignore.split('/')) - 1])
-        hg.clean(self.repo, self.currev)
-        for ignore in self.ignorelist:
-            shutil.copyfile(temp + ignore.split('/')[len(ignore.split('/')) - 1], ignore)
-            os.remove(temp + ignore.split('/')[len(ignore.split('/')) - 1])
-        pass
+        dlg = wx.MessageDialog(None, 'Revision Updates are recommended for Developers only.\n\nAre you sure to preform a Revision Update?\n\nNewer builds tend to have less bugs while older revisions may no longer function properly.', 'Expert Feature', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+        if dlg.ShowModal() == wx.ID_YES:
+            dlg.Destroy()
+            filename = 'ignorelist.txt'
+            self.filename = dir_struct["home"] + 'upmana' + os.sep + filename
+            component.get('validate').config_file(filename, "default_ignorelist.txt")
+            self.mana = self.LoadDoc()
+            temp = dir_struct["home"] + 'upmana' + os.sep + 'tmp' + os.sep
+            for ignore in self.ignorelist:
+                shutil.copy(ignore, temp + ignore.split('/')[len(ignore.split('/')) - 1])
+            hg.clean(self.repo, self.currev)
+            for ignore in self.ignorelist:
+                shutil.copyfile(temp + ignore.split('/')[len(ignore.split('/')) - 1], ignore)
+                os.remove(temp + ignore.split('/')[len(ignore.split('/')) - 1])
+        else:
+            dlg.Destroy(); pass
 
     def LoadDoc(self):
         ignore = open(self.filename)