Help - Search - Members - Calendar
Full Version: Database Manager
StealthBot.net > StealthBot > Scripting and Plugins > Plugin Projects
FiftyToo
I have completed everything I initially set out to do. There is still some testing that needs to be done, but I have done some serious testing and resolved many bugs. The last thing I need to do is create the documentation for the XML and how to use the dynamic objects.

Overview
Everyone knows that a database is the proper place to save data. There are SOME occasions where a text file would be better suited, but this is rarely the case. One of the main reasons people use a textfile over a database is because of simplicity. It is very easy to read and write to a file, much easier than it is to create a database, tables and columns.

At a glance, it might seem that a textfile is better than a database because of the above reasons. The problem happens when you need to manipulate or interpret the data. Things like this are overly complex when you are dealing with a flat file, but a database can handle complex tasks like this with its eyes closed. You also get the ability to do some pretty amazing things with a database that are simply not possible using a text file (such as indexes, constraints, foreign keys, type safety, and data normalization).

This plugin attempts to bridge the gap for scripts and database. It provides a data access object (called DatabaseManager) and can read database schema information (tables/columns) from an XML file. It also has a framework to store your queries (with parameters) and give you the ability to execute these queries with very little effort.

Purpose


This plugin serves 3 key purposes:

1) This plugin exposes a data access layer that is easy and intuitive. There are currently 2 methods that are available to plugins.

RunQuery(queryText) - runs a query and returns the result as a recordset.
RunPQuery(queryText, params) - runs a parameterized query and returns the result as a recordset.

The RunPQuery method is used in the dynamic database classes that are generated from the XML schema.... read on smile.gif

2) This plugin allows you to define the database tables and columns inside an XML file. It will automagically create the table if it doesnt exist and add the columns. It will ALSO validate the schema on bot load, adding any NEW fields as necessary. All of this is logged inside the SchemaAudit table so a user (or a crafty developer) can use this information to help troubleshoot problem. This setup is ideal for people that need to change the schema when it comes time to release a new bot version.

It is also possible to add a column to an EXISTING table, even if its part of a different plugin! The DatabaseManager plugin will already have several tables and will maintain the data. For example, it has a Player table that stores the PlayerID, Username, and ClanID. Whenever someone joins the channel, the plugin manager will ensure that they exist in the database. This allows other plugins to use this information without repeating the same logic over and over. Other plugins can also add columns to the Player table, such as AdvancedGreet for the AGP plugin.

3) This plugin will generate a VBS class that contains public functions for calling all of the queries that are defined in the XML. These queries are executes using ADO's parameterized queries and is safe from SQL injection. The query can define a return type (RecordSet, Value, None) for the function as well. This gives the developer a single location for every query. This is important because it makes it very simple to change the data layer without hunting through hundreds of lines of VBS code.

Now for the best part..... ANY plugin can create an instance of this dynamic class for ANY OTHER plugin!! Lets wait a second for that to sink in..... ..... ..... .......................... Yep, pretty incredible. So you want to write a plugin that will increase someone's trivia score? No problem, just get the trivia's database object and call the AddPoints() method. Pass in the PlayerID (from the DatabaseManager's object) and the amount of points. This would take 3 lines of code.....

CODE
Set dbDatabase = DatabaseManager.GetDatabaseClass("utDatabase")
Set dbTrivia = DatabaseManager.GetDatabaseClass("trivia")
Call dbTrivia.AddPoints(dbDatabase.GetPlayer("FiftyToo")("PlayerID"), 5000)



Future Enhancements

Inline documentation for the queries. This would allow developers to add text to a query that would help other people integrate with it. This plugin will come with an XSLT that will be used to transform the XML file into nicely formatted HTML. We could then add a command for the bot that will open an IE window with the HTML whenever someone types /databasehelp trivia.

Integrated query browser and designer. I would like to create a form that would allow developers to type in a query and see the results in a datagrid. It will list all of the dynamic objects and their methods and allow them to execute the functions and view the results.

Database backup and restore points. Since every plugin has access to the database, every plugin can potentially PWN your data. The only thing worse then losing the database for a plugin is losing the database for EVERY plugin. Yah, we need to look into this for sure smile.gif

Uninstalling plugins. Since all schema changes are recorded in the audit table, it is possible to figure out what a plugin did to the database, and then reverse it (ie drop the table/columns). We could even take this one step further and create a NEW database from the all of the current XML files, copy over the data from the OLD database into the NEW database, then replace the OLD database with the NEW database. This will ensure that there is no stale data muckin up our database.

Better error handling. Nuff said....

Support for views and indexes. There is already code to create a primary key on AutoIncrement columns, but I would like to allow people to create an index on any column they wany.

Support for constraints. This would allow developers to create constraints on a column. A constraint is a little snippet of SQL code that will get processed whenever a row is added or modified. These come in handy when you are dealing with data integrity. You could create a constraint on the Birthday column that will disallow values that are greater than the current date. You could make a constraint for the points column that disallows negative values. Constraints are generally used to ensure that the data in the database is always correct.





Questions and comments are welcome and appreciated smile.gif


52



EDIT

It is now possible to control whether or not the code generation should create functions based off table definition alone. So far I have 2 functions, Add<TableName> and List<TableName>.... For example

CODE
<SBPlugin>
    <DatabaseSchema>
        <Tables>
            <Table Name="Alert">
                <Columns>
                    <Column Name="AlertID" Type="Integer" AutoIncrement="True" />
                    <Column Name="PlayerID" Type="Integer" />
                    <Column Name="AlertDate" Type="Date" />
                    <Column Name="Message" Type="VarWChar" Size="200" />
                </Columns>
            </Table>
        </Tables>
    </DatabaseSchema>
    <Queries />
</SBPlugin>


.... will yield ...
CODE
Class ut_alert_DatabaseObject
    Public Function ListAlert(whereClause)
        If whereClause = "" Then
            Set ListAlert = DatabaseManager.RunQuery("SELECT * FROM Alert")
        Else
            Set ListAlert = DatabaseManager.RunQuery("SELECT * FROM Alert WHERE " & whereClause & "")
        End If
    End Function
    Public Function AddAlert(f_PlayerID,f_AlertDate,f_Message)
        Dim params, retval
        params = Array(DatabaseManager.NewParameter("@PlayerID", f_PlayerID, ADOVBS.adInteger, 4, ADOVBS.adParamInput),DatabaseManager.NewParameter("@AlertDate", f_AlertDate, ADOVBS.adDate, 8, ADOVBS.adParamInput),DatabaseManager.NewParameter("@Message", f_Message, ADOVBS.adVarWChar, 200, ADOVBS.adParamInput))
        DatabaseManager.Verbose "PlayerID: " & f_PlayerID
        DatabaseManager.Verbose "AlertDate: " & f_AlertDate
        DatabaseManager.Verbose "Message: " & f_Message
        Call DatabaseManager.RunPQuery("INSERT INTO Alert (PlayerID,AlertDate,Message) VALUES (@PlayerID,@AlertDate,@Message)", params)
    End Function
End Class


I will add Delete<TableName>, <TableName>Exists, and Update<TableName>. For simple databases, these auto-generated functions will do everything, totally eliminating the need for the <Queries>


52
The-Black-Ninja
OMG IT'S HEEEEEEERE! biggrin.gif


Brb, playing with utDatabase.plug

The XML goes into the plugins folder, right?
FiftyToo
QUOTE(The-Black-Ninja @ Mar 12 2009, 12:54 AM) *

The XML goes into the plugins folder, right?


Yes. It must be named EXACTLY like the plugin, except with a .db.xml tacked on to the end.

52
The-Black-Ninja
QUOTE
[12:58:59 AM] Scripting runtime error '500' on "utdatabase_Event_Load" call in file "utDatabase.plug"
[12:58:59 AM] Variable is undefined.
Using latest beta, both files in the plugins folder, I haven't renamed either file.
FiftyToo
QUOTE(The-Black-Ninja @ Mar 12 2009, 01:00 AM) *

QUOTE
[12:58:59 AM] Scripting runtime error '500' on "utdatabase_Event_Load" call in file "utDatabase.plug"
[12:58:59 AM] Variable is undefined.
Using latest beta, both files in the plugins folder, I haven't renamed either file.


On line 105 and 106 of the plugin, set both of those variables to true. This should spit a TON of shit to the chat window, just post everything thats relevant please smile.gif

I am not able to reproduce the problem (although I found my own sad.gif ). There is a race condition.... the Event_Load() is getting fired before the plugin can create all of the tables, and the query fails in the Event_Load(). Reloading the script resolves the problem, i will work on a real fix soon.

52

EDIT

.: StealthBot Beta v2.6999 Development Release 10 by Stealth.

Thats my version.... perhaps i should upgrade....

52


EDIT AGAIN

Apparently my copy/paste skills are teh suck. Search for oDoTH and comment out any line that uses it. I copied the DatabaseWebHelper class from a different plugin and i left some references to objects that you wont have. I am still not able to reproduce the problem, but the verbose logging will get is in the right direction.
The-Black-Ninja
Take your time on the fix, I'm working on the form in the meantime. It's a nice design, easy to navigate. I'll try and post a mock-up on the group section soon and see how users like it.

QUOTE
[01:17:24 AM] Scanning XML files...
[01:17:24 AM] Loading E:\Progs\Stealthbots\TesterBot\plugins\utDatabase.plug.db.xml
[01:17:24 AM] Scanning XML files... Complete
[01:17:24 AM] Validating Schema...
[01:17:24 AM] Generating Class ut_utDatabase_DatabaseObject...
[01:17:24 AM] Generating Class ut_utDatabase_DatabaseObject... Complete
[01:17:24 AM] Creating table: Player
[01:17:24 AM] Scripting runtime error '500' on "utdatabase_Event_Load" call in file "utDatabase.plug"
[01:17:24 AM] Variable is undefined.
FiftyToo
The problem is in prepConnection(). The Case statements should have an ADOVBS. in front of them. I will post an updated version tomorrow once I verify it.

Snap... you are the man smile.gif


CODE


    '// Ensures that the connection to the database is alive and well.
    Private Function prepConnection()        
        Select Case p_conn.State
            Case ADOVBS.adStateClosed
                Call Me.Verbose("Connection = adStateClosed")
                p_conn.ConnectionString = p_connectionString
                p_conn.CursorLocation = ADOVBS.adUseClient
                p_conn.Open
                prepConnection = True
            Case ADOVBS.adStateOpen
                prepConnection = True
            Case ADOVBS.adStateConnecting
                Call Me.Verbose("ConnectionState = adStateConnecting")
                prepConnection = False
            Case ADOVBS.adStateExecuting
                Call Me.Verbose("ConnectionState = adStateExecuting")
                prepConnection = False
            Case ADOVBS.adStateFetching
                Call Me.Verbose("ConnectionState = adStateFetching")
                prepConnection = False
            Default
                Call Me.Verbose("ConnectionState is unknown.")
                prepConnection = False
        End Select
    End Function
    


52
The-Black-Ninja
QUOTE
[01:59:44 PM] Scanning XML files...
[01:59:44 PM] Loading E:\Progs\Stealthbots\TesterBot\plugins\utDatabase.plug.db.xml
[01:59:44 PM] Scanning XML files... Complete
[01:59:44 PM] Validating Schema...
[01:59:44 PM] Generating Class ut_utDatabase_DatabaseObject...
[01:59:44 PM] Generating Class ut_utDatabase_DatabaseObject... Complete
[01:59:44 PM] Creating table: Player
[01:59:44 PM] Connection = adStateClosed
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Player
[01:59:44 PM] @ColumnName:
[01:59:44 PM] @Prefix: utDatabase
[01:59:44 PM] @AuditAction: TABLE CREATED
[01:59:44 PM] TableName: Player
[01:59:44 PM] ColumnName:
[01:59:44 PM] Prefix: utDatabase
[01:59:44 PM] AuditAction: TABLE CREATED
[01:59:44 PM] Adding column: PlayerID
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Player
[01:59:44 PM] @ColumnName: PlayerID
[01:59:44 PM] @Prefix: utDatabase
[01:59:44 PM] @AuditAction: COLUMN CREATED
[01:59:44 PM] TableName: Player
[01:59:44 PM] ColumnName: PlayerID
[01:59:44 PM] Prefix: utDatabase
[01:59:44 PM] AuditAction: COLUMN CREATED
[01:59:44 PM] Adding primary key to column: PlayerID
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Player
[01:59:44 PM] @ColumnName: PlayerID
[01:59:44 PM] @Prefix: utDatabase
[01:59:44 PM] @AuditAction: PRIMARY KEY CREATED
[01:59:44 PM] TableName: Player
[01:59:44 PM] ColumnName: PlayerID
[01:59:44 PM] Prefix: utDatabase
[01:59:44 PM] AuditAction: PRIMARY KEY CREATED
[01:59:44 PM] Adding column: Username
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Player
[01:59:44 PM] @ColumnName: Username
[01:59:44 PM] @Prefix: utDatabase
[01:59:44 PM] @AuditAction: COLUMN CREATED
[01:59:44 PM] TableName: Player
[01:59:44 PM] ColumnName: Username
[01:59:44 PM] Prefix: utDatabase
[01:59:44 PM] AuditAction: COLUMN CREATED
[01:59:44 PM] Adding column: ClanID
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Player
[01:59:44 PM] @ColumnName: ClanID
[01:59:44 PM] @Prefix: utDatabase
[01:59:44 PM] @AuditAction: COLUMN CREATED
[01:59:44 PM] TableName: Player
[01:59:44 PM] ColumnName: ClanID
[01:59:44 PM] Prefix: utDatabase
[01:59:44 PM] AuditAction: COLUMN CREATED
[01:59:44 PM] Creating table: Clan
[01:59:44 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:44 PM] @TableName: Clan
[01:59:45 PM] @ColumnName:
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: TABLE CREATED
[01:59:45 PM] TableName: Clan
[01:59:45 PM] ColumnName:
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: TABLE CREATED
[01:59:45 PM] Adding column: ClanID
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Clan
[01:59:45 PM] @ColumnName: ClanID
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: COLUMN CREATED
[01:59:45 PM] TableName: Clan
[01:59:45 PM] ColumnName: ClanID
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: COLUMN CREATED
[01:59:45 PM] Adding primary key to column: ClanID
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Clan
[01:59:45 PM] @ColumnName: ClanID
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: PRIMARY KEY CREATED
[01:59:45 PM] TableName: Clan
[01:59:45 PM] ColumnName: ClanID
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: PRIMARY KEY CREATED
[01:59:45 PM] Adding column: ShortName
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Clan
[01:59:45 PM] @ColumnName: ShortName
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: COLUMN CREATED
[01:59:45 PM] TableName: Clan
[01:59:45 PM] ColumnName: ShortName
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: COLUMN CREATED
[01:59:45 PM] Adding column: Name
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Clan
[01:59:45 PM] @ColumnName: Name
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: COLUMN CREATED
[01:59:45 PM] TableName: Clan
[01:59:45 PM] ColumnName: Name
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: COLUMN CREATED
[01:59:45 PM] Creating table: Flag
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Flag
[01:59:45 PM] @ColumnName:
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: TABLE CREATED
[01:59:45 PM] TableName: Flag
[01:59:45 PM] ColumnName:
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: TABLE CREATED
[01:59:45 PM] Adding column: FlagID
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Flag
[01:59:45 PM] @ColumnName: FlagID
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: COLUMN CREATED
[01:59:45 PM] TableName: Flag
[01:59:45 PM] ColumnName: FlagID
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: COLUMN CREATED
[01:59:45 PM] Adding primary key to column: FlagID
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Flag
[01:59:45 PM] @ColumnName: FlagID
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: PRIMARY KEY CREATED
[01:59:45 PM] TableName: Flag
[01:59:45 PM] ColumnName: FlagID
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: PRIMARY KEY CREATED
[01:59:45 PM] Adding column: Flag
[01:59:45 PM] INSERT INTO SchemaAudit (TableName, ColumnName, Prefix, AuditAction, AuditDate) VALUES (@TableName, @ColumnName, @Prefix, @AuditAction, Now())
[01:59:45 PM] @TableName: Flag
[01:59:45 PM] @ColumnName: Flag
[01:59:45 PM] @Prefix: utDatabase
[01:59:45 PM] @AuditAction: COLUMN CREATED
[01:59:45 PM] TableName: Flag
[01:59:45 PM] ColumnName: Flag
[01:59:45 PM] Prefix: utDatabase
[01:59:45 PM] AuditAction: COLUMN CREATED
[01:59:45 PM] Validating Schema... Complete
[01:59:45 PM] SELECT C.* FROM Clan AS C WHERE C.ShortName = @ShortName
[01:59:45 PM] @ShortName:
[01:59:45 PM] Scripting runtime error '-2147217865' on "utdatabase_Event_Load" call in file "utDatabase.plug"
[01:59:45 PM] The Microsoft Jet database engine cannot find the input table or query 'Clan'. Make sure it exists and that its name is spelled correctly..

FiftyToo
QUOTE(FiftyToo @ Mar 12 2009, 01:12 AM) *

There is a race condition.... the Event_Load() is getting fired before the plugin can create all of the tables, and the query fails in the Event_Load(). Reloading the script resolves the problem, i will work on a real fix soon.


In order to simplify the database design, there needs to be a record in the Clan table with an empty string as the short name. The way I am checking if this record exists is happening before the table has been created. Reloading the script resolves the problem because the table will exist the second time.

I think I will allow developers to create a list of queries that will be executed when a table is created. This list would contain queries to populate the data for the table right after its created. I could also do the same thing for columns. This would let developers release a new version of a plugin that adds a new field, and could toss in some SQL that would populate the new data for the new column. Since its fairly common to require a table to have some default values, I would consider this enhancement a priority and I will work on it ASAP.

btw, to make it easy to test you can use the /exec command inside the bot...

QUOTE

/exec Set dbObject = DatabaseManager.GetDatabaseClass("utDatabase")
/exec AddChat vbGreen, dbObject.PlayerExists("FiftyToo")


The query browser / interface enhancement would simplify this and would allow you to simply select the function from a list and fill in the parameters.

52
riffruff
Can this be easily converted to be used with a MySQL database?
FiftyToo
QUOTE(riffruff @ Mar 12 2009, 03:16 PM) *

Can this be easily converted to be used with a MySQL database?


I believe you would only need to change the connection string. I have a MySQL and SQLServer environment I can test it with. I will add it to my list of TODO's.

The only issue I can see happening is if someone decides to use a type that works with MySQL but not JET (the weird types like binary, smalldate, guid, enum, etc).



EDIT

Turns out that ADOX only works with JET databases, so it would take a fair amount of effort to get this to work with any other provider. All of the ADO code should work just fine, but anything that uses ADOX would need to be rewritten to use DDL queries such as CREATE, ALTER, and DROP. Its not impossible, but its not something I am considering. If you would like to help, I could point you in the right direction to integrate the changes.

52
AbsoluteMSTR
QUOTE(riffruff @ Mar 12 2009, 02:16 PM) *

Can this be easily converted to be used with a MySQL database?


doing something like that, for mysql

just no error handling yet
FiftyToo
QUOTE(AbsoluteMSTR @ Mar 20 2009, 01:34 AM) *

QUOTE(riffruff @ Mar 12 2009, 02:16 PM) *

Can this be easily converted to be used with a MySQL database?


doing something like that, for mysql

just no error handling yet


Did you want to just help with this plugin? You could help make it compatible with MySQL. There is really only 1 function that would need manipulated, validateSchema.... oh, and tableExists and columnExists, but those should be super easy smile.gif Let me know if you are interested. The code is checked into the SVN if you would like to check it out first.

52
bog0024
QUOTE(FiftyToo @ Mar 22 2009, 02:24 PM) *

QUOTE(AbsoluteMSTR @ Mar 20 2009, 01:34 AM) *

QUOTE(riffruff @ Mar 12 2009, 02:16 PM) *

Can this be easily converted to be used with a MySQL database?


doing something like that, for mysql

just no error handling yet


Did you want to just help with this plugin? You could help make it compatible with MySQL. There is really only 1 function that would need manipulated, validateSchema.... oh, and tableExists and columnExists, but those should be super easy smile.gif Let me know if you are interested. The code is checked into the SVN if you would like to check it out first.

52

The plugin loaded with that prefix does not have a matching db.xml file. Prefix: utDatabase
[1:35:25 PM] The plugin loaded with that prefix does not have a matching db.xml file. Prefix: utDatabase
[1:35:25 PM] Load Call Error On File> E:\Program Files\StealthBot\plugins\DB.plug
[1:35:25 PM] Error Number: 424 Description: Object required
i get this
The-Black-Ninja
You can't use it with 2.6R3.
FiftyToo
E:\Program Files\StealthBot\plugins\DB.plug

The file should be named utDatabase.plug, and you also need to have utDatabase.plug.db.xml in the same folder. This is all going to change since it will no longer be a plugin, so most of this is moot anyways.

52
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2014 Invision Power Services, Inc.