Well, I've got a proof of concept script now that compares a current script in the Custom folder of the install directory to a version key file on a web server. If the versions don't match it then reads each line from the current version on the server and writes them over the old file, saves the world and restarts the server.
It works great. Currently it's written to only check one specific file in one specific location, but could be easily expanded and an interface added.
I lack the SQL knowledge to write it securely, so for now it works from raw text files. My SQL pilot system was seemed vulnerable to injection code from a submitted script. One (intentionally) bad script could overwrite the whole repository. That's the problem with open sourcing such a system... everyone can see how it works.
However, the idea of using plain text files is becoming more appealing, as anyone could theoretically add a file through an ftp interface, and even use an ftp to sync files as they're saved from VS for collaborative work.
I'm in the middle of 3 large projects at the moment, and 2 more for another modding community, but when time becomes available, I'll revisit the idea and see if we can't set up an unofficial repository.
Here is the code I'm using:
Code:
using System;
using System.IO;
using System.Collections;
using Server;
using System.Collections.Generic;
using Server.Network;
using Server.Mobiles;
using System.Net;
using System.Text;
namespace Server.Commands
{
public class GetVersion
{
public static void Initialize()
{
CommandSystem.Register("GetVersion", AccessLevel.Player, new CommandEventHandler(Get_Ver));
}
[Usage("GetVersion")]
[Description("Check current Falconry Version")]
public static void Get_Ver(CommandEventArgs e)
{
double currentversion = 0.0;
StringBuilder sb = new StringBuilder();
byte[] buf = new byte[8192];
HttpWebRequest request = (HttpWebRequest)
WebRequest.Create("http://www.thedevilsacolyte.com/version.info");
HttpWebResponse response = (HttpWebResponse)
request.GetResponse();
Stream resStream = response.GetResponseStream();
string tempString = null;
int count = 0;
do
{
count = resStream.Read(buf, 0, buf.Length);
if (count != 0)
{
tempString = Encoding.ASCII.GetString(buf, 0, count);
sb.Append(tempString);
}
}
while (count > 0);
double installedversion = 0.9;
string svc = sb.ToString();
currentversion = Convert.ToDouble(svc);
if (e.Mobile is PlayerMobile)
{
PlayerMobile pm = (PlayerMobile)e.Mobile;
if (currentversion > installedversion)
{
pm.SendMessage("There is a new version of the Falconry system available for download");
StringBuilder sb2 = new StringBuilder();
byte[] buf2 = new byte[8192];
HttpWebRequest request2 = (HttpWebRequest)
WebRequest.Create("http://www.thedevilsacolyte.com/Falcon.cs");
HttpWebResponse response2 = (HttpWebResponse)
request2.GetResponse();
Stream resStream2 = response2.GetResponseStream();
string tempString2 = null;
int count2 = 0;
using (System.IO.StreamWriter file2 = new System.IO.StreamWriter(@"Scripts\Custom\Falcon.cs"))
{
do
{
count2 = resStream2.Read(buf2, 0, buf2.Length);
if (count2 != 0)
{
tempString2 = Encoding.ASCII.GetString(buf2, 0, count2);
file2.WriteLine(tempString2);
}
}
while (count2 > 0);
file2.Close();
}
Misc.AutoSave.Save();
pm.SendMessage("Install complete, world saved. Please restart the server");
}
else if (currentversion == installedversion)
pm.SendMessage("You are running the most recent version of the Falconry script.");
else
pm.SendMessage("You are running an unofficial version of the Falconry script, you must update manually from the modder of the script.");
}
}
}
}
As you can see, it looks for a version value in "version.info" on my website. that file contains the text "1.0". It reads that and returns the value as type double. That is compared to another version variable (hardcoded at the moment, will have to write a scanUserFileforVersion method) of version "0.9". It sees the installed version is less than the current version on my server and replaces the old file, saves the worls and prompts the user to restart the server.
It's primitive, lacking in elegance, and definitely bloated, but it works.
Edit:
To do list: (mostly just notes to my self, but if anyone wants to tackle any of these, go for it.)
• Need to set Byte length to be variable (it cuts off long lines, appending the remainder to a new line)
• Need to write a CheckLocalVersion method to get installed version info.
• Setup some error check if directories don't exist, (allow user to customize their custom folder location), check if file exists
etc.
• Gumps
• Currently is a command, would function better if it initialized automatically when admin logs on.
• Create a standard description for all files in comments at the top, this can be read into the gump. This would list dependencies, version info, Name, scripts to be added that were not in last install version, support url etc.
• A Package Script that formats the above comments correctly for reading. Perhaps a code cleaner to set standard indents and formatting (optional, but helpful for collaborative projects)
• A formalized web server directory structure, possible admin interface for upload and file management.
• Create backups of replaced scripts locally.
• A roll-back version option
• Server-end tracking of downloaded scripts for support and feedback to author
• Author has their own protected scripts, assigned by random key system. switch over to sealed functions for access (currently public) to prevent scripts from overriding server address variables, write locations etc.
• Hardcode all saves as type .cs, limit access to directories outside of runUO folder. (I imagine some kid writing a "del *.*" in the autoexec.bat or worse if filetype and directory access isn't restricted.)
• Present code in Gump so people updating have a chance to review changes in more detail than a description (helps deter malicious code, lets admins check to see if there would be any catastrophic errors with their existing systems etc)
Each step is easy to implement, but looking at them all at once... I see why no one's undertaken this yet. A lot of code for such a simple function, but it may be worth the pay-off. I'll spend some more time on it and post my results.