Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which use a logographic writing system and need larger font sizes.
***/

/*{{{*/
body {font-size:0.8em;}

#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}

.subtitle {font-size:0.8em;}

.viewer table.listView {font-size:0.95em;}

.htmlarea .toolbarHA table {border:1px solid ButtonFace; margin:0em 0em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler deleteTiddler'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
Well, I worked on the !settime command for several hours over the past day. This is what I've learned. The server doesn't have the ability to push the current level time down to the client. The client gets the level time when it connects, and it will check for it when a level restarts, but mostly the time is kept by the client itself. I tracked some of this info down by looking at cg_draw.c:1828 which has the function CG_DrawTimerSecs that indicates where the client stores the time (in cgs.levelStartTime). I then searched through the code for where this is modified. It's set by the configString received from the server, when is parsed in CG_ConfigString. That is only called a few times - on connection and on game initialization. This seems odd, as the config string is also where sudden death is controlled, and that is purely server side. Annoying, but true.

I still hold out hope that this command can be done, somehow, but it's going to take someone smarter than me to figure it out :)

!!!Update About an hour later
So, I didn't give up. I have a hard time doing that. By keeping on the track of the Configstring, looked into the instruction where the config string is modified. What I wanted to know was if a modification on the server side was always coupled with an update to the client. I figured it should be, unless the client polls for new information occasionally...but even then, I should have a hook into changing the level start time. It should work either way. I just kept thinking about it and knew that since the client side stores the startTime that it receives from the server, it must be transmitted from the server, and if it is transmitted, it just might be possible the client will update the info even outside of a new connection or !reset. Well, I put a debug print in the sv_init.c::SV_SetConfigstring command. Turns out the server is almost constantly sending new config strings, principally the buildpoints, stages and spawns strings. Makes sense, they can change all the time. I was having a hard time figuring out what Configstrings were what since the game.qvm has a few #declares that have intelligent names, but when the .qvm makes a call into the binary, those names are lost and it becomes a simple array lookup using the #declares as an indexing value. Anyways, to make it simpler, I was having a hard time figuring out what information was being passed, even though I could now see all the information via my debug prints, since I was loosing the information labels. Eventually I happened on the idea of creating a new console command, showconfigstrings, which iterates through all of the configstrings and prints them if they are non-empty. After doing that, I used my !settime command as it was already working (it changed the level.startTime and printed out the old and new values) to see what new value the startTime took on. I then searched through the configstrings and found out that there is a single configstring //dedicated to the level start time//. Sheesh. I should have looked in bg_public.h a little more - it's right there, CS_LEVEL_START_TIME. Now all I have to do is make a call to trap_SetConfigstring and pass it CS_LEVEL_START_TIME and the new level start time. It then updates all the clients, and the time changes. Brilliant. Can't believe it took me this long to figure all of this out.
I was writing a blog to document how I wasn't going to be doing much more coding yesterday morning. Good thing I never finished it and posted it, all the information is now the opposite of what I said it was after my work this morning. Great.

What have I done? Mostly just gotten my code base to build again and gotten most of the basic changes to the admin system changed over and working. I have tested the following bit of admin.dat, and it seems to work (though I haven't yet checked anything in game...just run the server and had it not crash)
>[level]
>level   = 0
>name    = ^4Unknown Player
>flags   = iahC
>allow   = self_control bot
Next bunch of changes: making sure the help system is properly updated, then testing that the admin.dat file is properly loaded and allows different admin levels to use the commands I give them. After that, update the admin flags (since I retained the original admin flag system). Then test the script system (since I changed that, and need to change it even more). After that will be updating the bot command and getting it to properly tap into the working bot system. Then updating the bots. Then adding stats via a database...then...well..we'll cross all that when we come to it.
The new admin system is done and tested. Works great! I've very excited about it. I've uploaded the changes to the SVN. I've got a new server and I'm going to be deploying to that server once I get it up and running. Because I want to deploy on a new server soon, I'm going to be making some changes to my stated work flow - I'm not going to test the script system next, or the admin flags. I don't need them yet. I want to get the bots working better. So, I just need to get the bot commands working right, and I want to alphabetize the user command list. Then the server will go up.

Made some modifications today, slight, but there. Added the [BOT] tag to all bot players. Checked the bot cleanup code, since the server went down lastnight due to too many bots. Also got the server updated with a working !register command. The command was already there, I'm just using it now.

Yesterday I added base code for alien bots. They now just walk to the nearest player, which isn't much, but its something. I also added code to the human bots to go for enemy players. The problem now is the code that handles their target acquisition, aiming and firing doesn't always work. They fire about 1 second in 20 when they could be firing. Lots of problems there. But, this means the bots are now much more useful for players who get on and want to muck around a bit.

Tried finding devs in the IRC. Nothing.

Finally, cyrri was good enough to give me info on how to make AAS files. I now want to make them for maps other than ATCS, it'll make the bots MUCH more interesting. Here's what he said:

@@AAS files are created from bsp files, the ones you find in pk3's. so you don't need the original map file. you need a tool called bspc, i guess you can get it along with mapper tools like gtkradiant or q3map2. if not, it also is part of the quake3 engine source, so you can compile it yourself. put the bsp in the same directory as bspc, then do this:
Quote

bspc -bsp2aas MAPNAME.bsp -forcesidesvisible


this will take a while, up to some minutes for big maps. if no error occurs, there will be an AAS file. otherwise look into bspc.log for the error. some of the tremulous maps have small leaks, so no AAS can be created. iirc tremor and niveus don't have such issues.
next week i'll have a look at where i stoped with the framework and PM you or post on the forums. good luck so far :)@@
Sorry I haven't updated in a while - lots of homework. I'm working on way too many things right now. I'm trying to get a comments thing going here so that you can leave comments on the development as you watch it. Also, I'm moving to a new dev server that has a better connection and better always-on capabilities. Finally, we're trying to get more and more of the bot files worked out for other maps. We're doing okay now.

And I still have homework. And 2 jobs.

!!Update

I've started working on the Googlecode sight a lot more. It's going to be much better for handling the tasks/enhancements/defects, so I'm going to us it instead of this site for those things. In fact, I think I will retire this site entirely in favor of just working on that one, since it is much more publicly visible. You can find the site [[here|http://code.google.com/p/enderstremserver/]]
After some of my own modifications, I now have a bot based on andi christ (cyrr?)'s patch that will run out, kill eggs and run back and reload when necessary. I chose to go with that patch as the basis rather than the other patch because it uses the AAS system, which allows the bot to easily go out and find targets and kill them. The other patch is very very basic, and simply turns and follows other players. Anyways, I don't want to belabor that point.

After this modest success, I got to thinking: how does the openarena bots work? They have completely working bots with difficulty levels and everything. If I can study them a bit, maybe I can see how they make their calls into the botlib that is already incorporated into my code and learn how its structured and how to make the most of it. Well, I downloaded openarena. Their bot code is huge. It's just...huge. But, that's part of the wonderful thing, that they have written so much of it and it's pretty well modularized and has plenty of stuff I think I can use. Since it's open source I can take from it liberally can just copy it over. It'll be a lot of work to put it in and make modifications and get it all working, but it's exciting because it is a huge, working AI codebase that uses a lot of the ideas that I have had myself.
I've decided how I'm going to do the new admin flag system. I've also found where it needs to be done. All of the major admin flag system code is held in g_admin.c:G_admin_readconfig. Now, it currently uses a system where each admin level loads up a series of letters, and each command when its declared is given a letter that corresponds to permission to use that command. I think this is a small, inadequate system that worked before when there were few commands but won't work now that there are many. I'm going to move to a Apache-like system where you can allow and deny commands to each admin level, and where each command has a group that you can also allow or deny.

So, this will work by me changing all of the commands in the g_admin_cmds[] from

>"command", function, "flags", "description", "syntax"

to be

>"command", function, "group", "description", "syntax"

Where group is a space-separated lists of groups the command belongs to. The admin config file will then go from being of the form
>[level]
>level = <level>
>name = <name>
>flags = <flags>

To being of the form
>[level]
>level = <level>
>name = <name>
>allow = [space separated list of commands and groups]
>deny = [space separated list of commands and groups]

The question then becomes how to store the information. I'll probably just do it by mapping each command to a number. I'll assume there won't be more than 255 commands in the server so that I can give each admin group an array of characters, then put command numbers that are allowed for that admin level in a character, but store it as an int from 0 - 255. When it checks if a command is allowed, it will do so by iterating through the array and checking for the number. Pretty decent, I'd say.

!!!Scripts
As I was looking at g_admin.h towards the top and writing out descriptions of the different structures there, I realized that we had g_admin_cmd_t and g_admin_command_t. Crappy design, really. Then I thought more about it and realized that g_admin_command_t was the structure based around the admin.dat commands - the ones that are defined by an external file. Makes sense to me to call these 'scripts' because that's what they are. So, I changed the name of the structure as well as the syntax of the admin.dat file to correspond to the new naming - it is now [script] instead of [command], but they underlying syntax is the same.
I'm still in the midst of changing over the admin system. Things on this are going to slow down for a while because my homework and other projects are picking up for a while, so ... I'll get back to this later. Hopefully, a couple days.
I've started making some significant changes to how the admin system works. Currently, I'm working on changing the admin.dat file so that it now takes commands of the form
>[level]
>level   = 0
>name    = ^4Unknown Player
>flags   = iahC
>allow   = self_control bot
I may eventually add a 'deny' command, but for now allow is enough. Allow will accept groups and commands, where each command belongs to one and only one group. That portion is all done. In fact, near as I can tell, the parser works also, so the only thing left to do to check this whole thing off is update the helper commands that are used by the help system to check if a given player has permission for a given command. Then I test it. I will try to keep this Tiddler up to date with the latest info on how my admin.dat file works - I may end up making more and more changes too, including changing some of the admin flags.

!!!Feb 12 2008
Updated the SVN, rev 5, and it has a working system as described above. There are currently a few things to add to it. First, I'd like an 'all' group built-in.
!Introduction
This tiddler is for showing what my current coding priority is, so that the community can know all the time where my attention is. It should be constantly updated with the last time that I looked at this tiddler and what my priorities are.

!!Feb 15, 2008
* Move over the new layouts from the old server to the new
* Get AAS files for other builtin maps. If I can't get them for builtin, make them for other good maps.
* Flesh out AdminSystem so that it has a built-in 'all' group that contains a reference to all of the commands
* Fix the bot underpinnings so that we don't get people connecting, making a million bots, then leaving and the bots stick around doing nothing
WelcomePage
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
 major: 1, minor: 1, revision: 0, 
 date: new Date("mar 17, 2007"), 
 source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};

if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};

bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
 if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){ 
  url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
 }
 return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
[[WelcomeToTiddlyspot]] 
WelcomePage
TremulousServerInfo
TremServerCodeStatus
TremServerIdeas
TremServerAIStatus
TremServerCodeNotes
CurrentCodingPriority
<<newJournal "DD MMM YYYY, hh:mm">>
Made this while working on getting my server to build on my new game server. Here's the steps I had to take. I use Ubuntu exclusively, so your instructions will probably be radically different. What can I say? Get Ubuntu...
# Check out the sourcecode. This can be done with the following command
>svn checkout http://enderstremserver.googlecode.com/svn/trunk/ enderstremserver-read-only
# Get the required packages, if you don't have them
>sudo apt-get install libc6-dev libsdl1.2-dev libopenal-dev
# Go to the directory you just downloaded the SVN to and make it
>make

The end, all built. That was hard, wasn't it?
 
moving towards Tremulous 1.2 by my onesie
STFU-Ender TremulousServer TiddlyWiki
!Introduction
Because the AI code is such a huge project, I'm going to try to keep the information in this Tiddler. This is where I will flesh out ideas and designs before I implement them so that I have a large-scale, coherent picture of how AI will be done.

!!Jan 31 2008
Now that I have a working bot code base, I'm looking into modifying the base AI code. Right now its...well...laughably simple. All of the middle-level logic is handled within g_bot.c:G_BotThink. There is no differentiation between Aliens and Humans, half of the commands are present but not implemented at all...that's not surprising though, it is a 0.1alpha release. The low-level logic is all handled by the AI library, which is very nice because I'd rather not have to code much low-level logic. I'm pretty sure I will end up doing it if I advance the AI enough that I'm considering how to handle building. There's absolutely no concept of high-level logic in the code. So, there's much work to be done.

First, I need to split the code up into Aliens and Humans. The AI should be substantially different for both. Next, I need to create some intermediary middle-level logic functions so that I don't end up with one huge G_BotThink command. I also would like to move the BotThink from a middle-level to a high-level logic. Now, these aren't some neat terms from AI, they are crude categorizations I made up while thinking about AI. Here's the way I see it:

* Low-level: Any logic having to do with the mechanics of seeing an enemy, shooting at the enemy, buying weapons, etc.
* Mid-level: Logic that is composed of many low-level logic blocks. For instance, following a teammate and attacking any noticed enemies.
* High-level: Logic that is composed of many middle-level logic blocks. For instance, logic that decides if the bot is going to continue chasing an enemy, run back to base, build, attack the enemy base, etc.

So, the general idea right now is for me to create a G_BotThinkHumans and G_BotThinkAliens command. In the G_BotThink commands I will have a switch statement that changes the high-level behavior based on what command the bot was last given and its difficulty level. This will then filter down into many functions in two new files, g_humanbot.c and g_alienbot.c. I think this will improve organization.

For now, I have decided on creating the following AI functions. They are in all caps because they are enums that the G_BotThink switches on

!!Feb 13 2008
I switched my bot code base over to a totally different system. Like I said, the one I was using was laughably simple, and as such would have required an enormous effort on my part to get it to do anything intelligent. I've now taken a great deal of code from a patch created by andichrist, who, it appears, took his code from OpenArena. The code uses a .aas file system where the file encodes a great deal of level information that the bot uses to intelligently navigate the level. As of right now, the human bot can go out, find eggs, destroy them, return to base and reload, and repeat. It's not flashy or super involved, but it at least does 3 things: navigates the level without me having to tell it to, hits a target with minimal code and can find its own base structures. With those things done, I now have basically all of the low-level logic in place that I need. It's a matter of creating middle- and high- level logic.

As such, I've pulled out the old status table. It was really only suited for the previous bot AI code base. This new code base will need a new plan to get it developed. At this point, I plan to have a !bot command that allows players to add and remove bots. There's a !botlib command that controls some of the botlib variables. I will likely add the following commands as part of a new command, !orderbot, which will be of the form
>!orderbot <name> <order>

!Middle-level Logic Tasks
|!Order|!Description|!Status|
| Hold | Does absolutely nothing but stand there | @@color(green): Done, obviously@@ |
| TargetPractice | Move to a pre-specified location and stand there | @@color(red): Not started@@ |
| Dodge | Move to a pre-specified location and dodge around | @@color(red): Not started@@ |
| DodgeAndAttack | Move to a pre-specified location and dodge around and fire on enemies | @@color(red): Not started@@ |
| Attack | Attack the enemy | @@color(red): Not started@@ |
| Defend | Defend the base | @@color(red): Not started@@ |
| Follow | Follow a friend and protect them | @@color(red): Not started@@ |
| FollowAndAttack | Follow a friend and attack enemies | @@color(red): Not started@@ |
| FollowIdle | Follow a friend and do nothing | @@color(red): Not started@@ |
| Build | The piece-de-resistance: a bot that can intelligently build a base | @@color(red): Not done. Oooooh, no@@ |
| Repair | Repair damaged portions of the base | @@color(red): Not started@@ |
| SetSkill | Set the skill level of the bot - how often it misses, looses track of enemies, etc. | @@color(red): Not started@@ |

!Alien Tasks
There's also going to be a host of modifications to the low-level logic for the aliens. The human side is mostly done, thanks to OpenArena. Here's my initial thoughts on what needs to be done for the aliens

|!Short Description|!Long Description|!Status|
| Dodging | Dodging fire such that their movements are not entirely predictable | @@color(red): Not started @@ |
| TakingCover | Taking cover when under fire or when they need to heal | @@color(red): Not started @@ |
| Wallwalk | Handling wallwalking as a function of getting somewhere and dodging. This one is big | @@color(red): Not started @@ |
| HeadBites | Going for the head, but only doing it as a percentage based on skill level | @@color(red): Not started @@ |
| BasiliskGrab | Programming the basilisk to get in close, grab from behind, get on the head, etc. | @@color(red): Not started @@ |
| MaruaderWallJump | Programming the marauder to walljump. Just bouncing off a second wall is enough, but to get them to use it to navigate a level would be fantastic | @@color(red): Not started @@ |
| GoonPouncingAttack | Getting a dragoon to use a pounce, especially to use the pounce fly-by technique | @@color(red): Not started @@ |
| GoonPouncingMovement | Getting the goon to move, ala MarauderWallJump | @@color(red): Not started @@ |
| TyrantTrample | If the gone attacking is done, this will be trivial | @@color(red): Not started @@ |
!Intro
This page is where I'll be keeping notes to myself as I make progress on the code. I'm on the move a lot during the day, so often development is interrupted by me getting on a train, bus, going to class, etc. This is where I 'hibernate' my brain in the middle of coding so I don't loose track of my progress

!Notes
!!!Jan 15ish 2008
I've been searching around for where I could add other commands - I already understand how to add server commands such as !<command>, but I'd like to add commands like /teamstatus. I know D*S Main does it somehow, it's just a matter of figuring out how. So far, I can find several places where commands are added:
* client/cl_main.c:26xx
* client/snd_main.c:38x
* client/cl_cgame.c:180
* client/cl_keys:108x
* client/cl_console.c:31x
* client/cl_input.c:8xx
* qcommon/cmd.c:6xx, 76x
* qcommon/vm.c:7x
* qcommon/files.c:28xx
* qcommon/common.c:155x, 238x
* qcommon/cvar.c:94x
* renderer/tr_init.c:113x
* server/sv_ccmds.c:73x
This came about by searching for the function "Cmd_AddCommand". Now, here's some things I've figured out. I've tried adding a command help to the server file, server/sv_ccmds.cc, but the command doesn't seem to register. I believe that is because after adding it to source and building, the only change produced by make was in the binary, and I don't run the built binaries.

Starting a dedicated server and a regular client I tried some of the commands from the client that I found in various places - I'm trying to understand in what modes of the game I can use which commands. The commands 'hunklog' and 'zonelog' found in qcommon/common.c aren't available in devmap or connected to a dedicated server. They aren't even available in the server. 'fdir' and 'dir' are available in devmap or connected (from qcommon/files.c). 'vminfo' is available in both (from qcommon/vm.c)

Aha! 'cmdlist' shows all available commands in any given mode. Now it's just a matter of using that to figure out which files correspond to which types of commands

I think I just need to modify g_cmds instead of g_svcmds!
The Tremulous server is on port 9802 and is currently running v5.2 of Lakitu7's game.qvm. It is not currently utilizing all of the features provided by the QVM, but that should change shortly. There are currently 8 normal clients and 1 reserved client allowed to connect. I have created one admin of the server, who is a level 3, cerebral, but I expect to create more. This should go hand-in-hand with the MumbleServer.

!Things to add to the server
|!Command|!Effect|!Status|
| {{{ !blibset }}} | Sets a variable in the bot lib | @@color(orange): Command started @@ |
| {{{ !bot }}} | Gives bot a command - changes its behavior |  @@color(orange): see TremServerAIStatus @@ |
| {{{ !buildstatus }}} | Dummy command, redirects to /buildstatus | @@color(orange): Command started @@ |
| {{{ !clinic }}} | Shows info on the next clinic | @@color(yellow): Shows info now, would like the info more easily changeable @@ |
| {{{ !gamestats }}} | Shows team/game stats/awards at end game |  @@color(orange): Command started @@ |
| {{{ !gamestatus }}} | Dummy command, redirects to /gamestatus | @@color(orange): Command started @@ |
| {{{ !give }}} | Give credits/evos to player | @@color(yellow):Done and tested, want to add giving weapons @@ |
| {{{ !help }}} | Improvement to the messages for !help | @@color(green): Done and tested @@ |
| {{{ !layoutdelete }}} | Deletes a given layout | @@color(orange): Command started @@ |
| {{{ !layoutmode }}} | Sets the server to layout-friendly mode. Instant build, high stages, can decon last node | @@color(red): Not started @@ |
| {{{ !layoutload }}} | Loads a given layout without changing teams | @@color(orange): Command started @@ |
| {{{ !mumble }}} | Shows mumble server info | @@color(yellow): Shows some info, would like to add players connected to server @@ |
| {{{ !normalize }}} | Sets the server to normal operation | @@color(orange): Command started @@ |
| {{{ !playerstats }}} | check other player's stats command, instead of just /mystats | @@color(orange): Command started @@ |
| {{{ !rcon }}} | prevents embarrassing/dangerous !rcon calls | @@color(green): Done and tested @@ |
| {{{ !register }}} | Registers the player so their name is reserved and so they have access to more commands | @@color(orange): Command started but as 'L1' @@ |
| {{{ !report }}} | Reports admin abuse | @@color(orange):Command started @@ |
| {{{ !setstage }}} | Quick way to stage up/down | @@color(green): Done and tested @@ |
| {{{ !settime }}} | Rollback level timer to avoid SD | @@color(green): Done and tested. @@ |
| {{{ !take }}} | Take away guns/evo/credits | @@color(yellow):Taking evos/credits works, guns no @@ |
| {{{ !toggleinstantbuild }}} | turn on instant building | @@color(green):Done and tested @@ |
| {{{ !warn }}} | Warn players about behavior | @@color(blue):Done not tested @@ |
| {{{ !website }}} | Shows info on the website | @@color(green): Done and tested @@ |
| {{{ !XvsAll }}} | Player X versus all players on team Y | @@color(orange):Command started @@ |
| {{{ /buildstatus }}} | shows all buildings and their current distance away from the builder (maybe direction, we'll see) | @@color(orange): Command started @@ |
| {{{ /defensestatus }}} | shows teammates in base, enemies in base, teammates engaged in battle, etc | @@color(red): Not started @@ |
| {{{ /gamestatus }}} | shows stages, team kills, num team members | @@color(orange): Command started @@ |
| {{{ /help }}} | Shows !help-like info for /commands | @@color(green): Done and tested @@ |
| {{{ /teamstatus }}} | Tells status ala DS Main + a little functionality | @@color(green):Done and tested @@ |


!Features of the server to add/enable/change
|!Variable(s)|!Effect|!Status|
| {{{ g_tellBuildingDestroyed }}} | Announce to players when anything happens - buildings built, deconned, destroyed, etc. 1 - defending team 2- attacking 3 - both | @@color(green): Done and tested @@ |
| {{{ sv_allowUnpureAdminLevel }}} | Allow admin level x to be unpure | @@color(red): Not started - server-side change @@ |
| {{{ g_showKillerHP }}} | Show killer stats outside of devmap | @@color(green): Done and tested @@ |
| {{{ g_BPPerPlayer }}} | Dynamic BP | @@color(orange): Variable done@@ |
| {{{ sv_announcementX(0-9), sv_announcementInterval }}} | website/mumble/clinic time display a intervals | @@color(green): Done and tested @@ |
| {{{ sv_autoTeamLeader }}} | auto team-leader used for designating, kicking, etc. | @@color(orange): Variable done @@ |
| {{{ sv_welcomeMessage }}} | Message printed to clients when they connect | @@color(blue): Done and tested, need to fix newlines @@ |
| {{{ sv_dynamicSpectatorView}}} | Automagically moves the spectator view to watch their base, not the map location | @@color(red): Not started @@ |

!Large-scope changes
|!Change|!Status|
| Change the admin flag system so that it allows for more than 36 flags (letters + numbers) | @@color(green): See AdminSystem @@ |
| Add bots to the server | @@color(yellow): Bots working, need more smarts @@ |
| .AAS files for every builtin map | @@color(orange): Finished ATCS @@ |
| Add XP to the server | @@color(red): Not started @@ |
| Add Mumble server patching | @@color(red): Not started @@ |
| Figure out admin level abilities, Lvl 1 registration, etc | @@color(red): Not started @@ |
| Layout system - randomly loads a layout, can specify a layout in rotation, can conditionally load a layout...? | @@color(yellow): Random layouts and in rotation are working, no conditionals yet @@ |
| Attack groups - a new chat mode for groups, showing their ammo/health/equipment | @@color(red): Not started @@ |
| Allow spectators to place buildings in layout mode - makes layouts MUCH easier | @@color(red): Not started @@ |
| Get this code match up with the changes in the SVN | @@color(red): Not started @@ |
| Interesting and varied death messages | @@color(red): Not started, see http://tremulous.net/forum/index.php?topic=7268.0 @@ |
| Cool visual cue when stage changes - models, skins, etc | @@color(red): Not started, see http://tremulous.net/forum/index.php?topic=7132.0 @@|
| Screaming when grabbed by a basilisk/shocked by mara | @@color(red): Not started, see http://tremulous.net/forum/index.php?board=3.30 @@ |
| Visual cue when attacking with aliens | @@color(red): Not started, see http://tremulous.net/forum/index.php?topic=7147.0 @@ |
| Lucifer cannon feedback - other players hear it charge, visible shot changes, charge meter for client | @@color(red): Not started @@ |
| Clan badges on player models, above allied players | @@color(red): Not started, see http://tremulous.net/forum/index.php?topic=6537.0 @@ |
| Visual cue when a base structure is hit | @@color(red): Not started @@ |
| Crouch toggle | @@color(red): Not started @@ |
| Quake-style +forward and -forward commands - turns everything into a toggle | @@color(red): Not started @@ |
| Alien headshots/legshots for larger classes (mara on up) | @@color(red): Not started @@ |
| Wallwalk player models/buildings | @@color(red): Not started @@ |
| Allow evolve when near OM, no matter what | @@color(red): Not started @@ |
| Location tag in HUD | @@color(red): Not started @@ |

!Stuff added already
!!!Jan 14 2008
Just added a new variable, g_showKillerHP, which takes the place of g_devmapKillerHP. Also added g_devmapNoGive which removes the give commands on devmaps if set > 0. I also updated the give messages in g_cmds.c->Cmd_Give_f

!!!Jan 15 2008
Added a new variable, g_tellDestroyedBuildings which, when set to 1, teamsays to the attacking team when a building is destroyed. When set to 2 it tells the defending team when a building is destroyed. 3 teamtells both teams.I still haven't added this message to all building die methods in g_buildable.c...

!!!Jan 23 2008
Added a new function to server/sv_ccmds.cc - SV_Help_f, as well as a matching line in SV_AddOperatorCommands that adds a new /help command to the server. The problem is, this source appears to only update the various binary files, not the qvm, so it's hard to add to a server since my server now works on protocol 70, not 69 as it should. Because of that, changes to the binary can't be reflected in my server unless I do some drastic changes.

!!!Feb 1 2008
Posted some test results for !toggleInstantBuild and !setstage, also the 'impossible' message for !settime

!!!Feb 4 2008
Updated info on the map system - just got the ability to specify layouts in the rotation file. Sweet.

Also added the design of the new admin system and the task to get this code up to SVN compliance for the latest version.

!!!Feb 10 2008
I got tired of adding these little blurbs to the end of this one tiddler - you can now find code updates in the journals, which are only accessible via the timeline in the bottom right hand corner. Eventually I'll try to set up a dynamic tiddler that will show the latest journal entries.
!Introduction
This is where I will keep any ideas that I may or may not want to implement someday. Think of it as a sort of development sandbox. Ideas that have graduated to something that I actually want to do are generally moved to the TremServerCodeStatus page.

!!Jan 31, 2008
I'm considering the impact of adding more functionality to the human helmet. Currently, it just adds radar. I can think of ''lots'' of other things for it to do. For one, it could add a dynamic targeting reticle, something that shows a box around aliens when they are spotted (fits their bounding box, of course). When other players with helmets are in range, it also allows the other player to see the same box from their vantage point. What does this mean? Well, if an alien is hiding behind some cover, and one human in a 3-man strikeforce sees it, the other two humans would also then see a bounding box around the alien //through the cover//. I think this would not only look cool and add more use to the helmet, but also make Tremulous look like a very advanced strategy game.

Of course, then you'd have to add a similarly powerful alien enhancement. My pick? Adding alien teammates and alien base structures to their sensor radar. Or, if that's not good enough for you, seeing humans through walls (as some sort of infrared image...cool) when they are within X distance of the Overmind. Cool.

!!Feb 13 2008
Yesterday I took a look at the Mercenaries Guild 1.2 test server. There's some additions there that are really nice. The new creep looks good, I love the new barricades, love the charge bar for the dragoons/rants and the new rant charge damage system.


!Feb 22, 2008
Better feedback on commands like '!map' - it should tell you what  map you're moving to and who did it.
In spite of the fact that I loved all of those things, for the sake of...well...politics, I'm going to limit my 1.2 release to not have any major gameplay changes. I would have loved to have done it, but I think I'll wait for another version for that. I'd like to incorporate just about all of the MG 1.2 changes to my release - except for the dragoon barb splash damage. It's way overpowered.

My TremulousServer is called

@@color(green):{@@STFU@@color(green):}@@Ender's Testing Server

and can be found at ender27182818.dyndns.org:9802. You can find the latest information on the server at my [[Dretch*Storm|http://dretchstorm.com]] group, [[Mentors|http://dretchstorm.com/node/2696]]

I also run a MumbleServer. You can find the most up-to-date information on it at [[my Dretch*Storm page|http://dretchstorm.com/node/2704]]

One thing that you should realize is that while you can track the day-to-day and minute-to-minute updates to the tremulous code here, my server does not reflect the recent code changes. I run a local development server on my laptop where I make all the changes and test them. The server code gets updated weekly, for the most part, though that may change as I have just moved to a Linux server which is much easier to administer.
/***
Contains the stuff you need to use Tiddlyspot
Note you must also have UploadPlugin installed
***/
//{{{

// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'stfu-ender';

// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)

// disable autosave in d3
if (window.location.protocol != "file:")
	config.options.chkGTDLazyAutoSave = false;

// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
	SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
	SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
	OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
	DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
	MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}

// create some shadow tiddler content
merge(config.shadowTiddlers,{

'WelcomeToTiddlyspot':[
 "This document is a ~TiddlyWiki from tiddlyspot.com.  A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below.  Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
 "<<tiddler TspotControls>>",
 "See also GettingStarted.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick.  You can make changes and save them locally without being connected to the Internet.  When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]].  Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help.  If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
 "",
 "@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site.  Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),

'TspotControls':[
 "| tiddlyspot password:|<<option pasUploadPassword>>|",
 "| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<<br>>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
 "| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),

'TspotSidebar':[
 "<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . .  " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),

'TspotOptions':[
 "tiddlyspot password:",
 "<<option pasUploadPassword>>",
 ""
].join("\n")

});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 30/01/2008 16:02:20 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 30/01/2008 17:36:12 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 31/01/2008 08:35:56 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 05/02/2008 13:07:59 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 12/02/2008 08:07:18 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 13/02/2008 08:51:11 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 13/02/2008 09:20:08 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 13/02/2008 10:21:49 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | ok |
| 27/02/2008 09:15:49 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . | failed |
| 27/02/2008 09:16:18 | STFU-Ender | [[TremServer_tiddlywiki.html|file:///home/eli/tiddlywiki/TremServer_tiddlywiki.html]] | [[store.cgi|http://stfu-ender.tiddlyspot.com/store.cgi]] | . | [[index.html | http://stfu-ender.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
	major: 1, minor: 0, revision: 2, 
	date: new Date("Apr 19, 2007"),
	source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
	coreVersion: '2.2.0 (Beta 5)'
};

config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");

merge(config.macros.option.types, {
	'pas': {
		elementType: "input",
		valueField: "value",
		eventName: "onkeyup",
		className: "pasOptionInput",
		typeValue: config.macros.option.passwordInputType,
		create: function(place,type,opt,className,desc) {
			// password field
			config.macros.option.genericCreate(place,'pas',opt,className,desc);
			// checkbox linked with this password "save this password on this computer"
			config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);			
			// text savePasswordCheckboxLabel
			place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
		},
		onChange: config.macros.option.genericOnChange
	}
});

merge(config.optionHandlers['chk'], {
	get: function(name) {
		// is there an option linked with this chk ?
		var opt = name.substr(3);
		if (config.options[opt]) 
			saveOptionCookie(opt);
		return config.options[name] ? "true" : "false";
	}
});

merge(config.optionHandlers, {
	'pas': {
 		get: function(name) {
			if (config.options["chk"+name]) {
				return encodeCookie(config.options[name].toString());
			} else {
				return "";
			}
		},
		set: function(name,value) {config.options[name] = decodeCookie(value);}
	}
});

// need to reload options to load passwordOptions
loadOptionsCookie();

/*
if (!config.options['pasPassword'])
	config.options['pasPassword'] = '';

merge(config.optionsDesc,{
		pasPassword: "Test password"
	});
*/
//}}}

/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.0|
|''Date:''|May 5, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (#3125)|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
	major: 4, minor: 1, revision: 0,
	date: new Date("May 5, 2007"),
	source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
	author: 'BidiX (BidiX (at) bidix (dot) info',
	coreVersion: '2.2.0 (#3125)'
};

//
// Environment
//

if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false;	// true to activate both in Plugin and UploadService
	
//
// Upload Macro
//

config.macros.upload = {
// default values
	defaultBackupDir: '',	//no backup
	defaultStoreScript: "store.php",
	defaultToFilename: "index.html",
	defaultUploadDir: ".",
	authenticateUser: true	// UploadService Authenticate User
};
	
config.macros.upload.label = {
	promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
	promptParamMacro: "Save and Upload this TiddlyWiki in %0",
	saveLabel: "save to web", 
	saveToDisk: "save to disk",
	uploadLabel: "upload"	
};

config.macros.upload.messages = {
	noStoreUrl: "No store URL in parmeters or options",
	usernameOrPasswordMissing: "Username or password missing"
};

config.macros.upload.handler = function(place,macroName,params) {
	if (readOnly)
		return;
	var label;
	if (document.location.toString().substr(0,4) == "http") 
		label = this.label.saveLabel;
	else
		label = this.label.uploadLabel;
	var prompt;
	if (params[0]) {
		prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0], 
			(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
	} else {
		prompt = this.label.promptOption;
	}
	createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};

config.macros.upload.action = function(params)
{
		// for missing macro parameter set value from options
		var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
		var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
		var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
		var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
		var username = params[4] ? params[4] : config.options.txtUploadUserName;
		var password = config.options.pasUploadPassword; // for security reason no password as macro parameter	
		// for still missing parameter set default value
		if ((!storeUrl) && (document.location.toString().substr(0,4) == "http")) 
			storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
		if (storeUrl.substr(0,4) != "http")
			storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
		if (!toFilename)
			toFilename = bidix.basename(window.location.toString());
		if (!toFilename)
			toFilename = config.macros.upload.defaultToFilename;
		if (!uploadDir)
			uploadDir = config.macros.upload.defaultUploadDir;
		if (!backupDir)
			backupDir = config.macros.upload.defaultBackupDir;
		// report error if still missing
		if (!storeUrl) {
			alert(config.macros.upload.messages.noStoreUrl);
			clearMessage();
			return false;
		}
		if (config.macros.upload.authenticateUser && (!username || !password)) {
			alert(config.macros.upload.messages.usernameOrPasswordMissing);
			clearMessage();
			return false;
		}
		bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password); 
		return false; 
};

config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir) 
{
	if (!storeUrl)
		return null;
		var dest = bidix.dirname(storeUrl);
		if (uploadDir && uploadDir != '.')
			dest = dest + '/' + uploadDir;
		dest = dest + '/' + toFilename;
	return dest;
};

//
// uploadOptions Macro
//

config.macros.uploadOptions = {
	handler: function(place,macroName,params) {
		var wizard = new Wizard();
		wizard.createWizard(place,this.wizardTitle);
		wizard.addStep(this.step1Title,this.step1Html);
		var markList = wizard.getElement("markList");
		var listWrapper = document.createElement("div");
		markList.parentNode.insertBefore(listWrapper,markList);
		wizard.setValue("listWrapper",listWrapper);
		this.refreshOptions(listWrapper,false);
		var uploadCaption;
		if (document.location.toString().substr(0,4) == "http") 
			uploadCaption = config.macros.upload.label.saveLabel;
		else
			uploadCaption = config.macros.upload.label.uploadLabel;
		
		wizard.setButtons([
				{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption, 
					onClick: config.macros.upload.action},
				{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
				
			]);
	},
	refreshOptions: function(listWrapper) {
		var uploadOpts = [
			"txtUploadUserName",
			"pasUploadPassword",
			"txtUploadStoreUrl",
			"txtUploadDir",
			"txtUploadFilename",
			"txtUploadBackupDir",
			"chkUploadLog",
			"txtUploadLogMaxLine",
			]
		var opts = [];
		for(i=0; i<uploadOpts.length; i++) {
			var opt = {};
			opts.push()
			opt.option = "";
			n = uploadOpts[i];
			opt.name = n;
			opt.lowlight = !config.optionsDesc[n];
			opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
			opts.push(opt);
		}
		var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
		for(n=0; n<opts.length; n++) {
			var type = opts[n].name.substr(0,3);
			var h = config.macros.option.types[type];
			if (h && h.create) {
				h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
			}
		}
		
	},
	onCancel: function(e)
	{
		backstage.switchTab(null);
		return false;
	},
	
	wizardTitle: "Upload with options",
	step1Title: "These options are saved in cookies in your browser",
	step1Html: "<input type='hidden' name='markList'></input><br>",
	cancelButton: "Cancel",
	cancelButtonPrompt: "Cancel prompt",
	listViewTemplate: {
		columns: [
			{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
			{name: 'Option', field: 'option', title: "Option", type: 'String'},
			{name: 'Name', field: 'name', title: "Name", type: 'String'}
			],
		rowClasses: [
			{className: 'lowlight', field: 'lowlight'} 
			]}
}

//
// upload functions
//

if (!bidix.upload) bidix.upload = {};

if (!bidix.upload.messages) bidix.upload.messages = {
	//from saving
	invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
	backupSaved: "Backup saved",
	backupFailed: "Failed to upload backup file",
	rssSaved: "RSS feed uploaded",
	rssFailed: "Failed to upload RSS feed file",
	emptySaved: "Empty template uploaded",
	emptyFailed: "Failed to upload empty template file",
	mainSaved: "Main TiddlyWiki file uploaded",
	mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
	//specific upload
	loadOriginalHttpPostError: "Can't get original file",
	aboutToSaveOnHttpPost: 'About to upload on %0 ...',
	storePhpNotFound: "The store script '%0' was not found."
};

bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
	var callback = function(status,uploadParams,original,url,xhr) {
		if (!status) {
			displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
			return;
		}
		if (bidix.debugMode) 
			alert(original.substr(0,500)+"\n...");
		// Locate the storeArea div's 
		var posDiv = locateStoreArea(original);
		if((posDiv[0] == -1) || (posDiv[1] == -1)) {
			alert(config.messages.invalidFileError.format([localPath]));
			return;
		}
		bidix.upload.uploadRss(uploadParams,original,posDiv);
	};
	
	if(onlyIfDirty && !store.isDirty())
		return;
	clearMessage();
	// save on localdisk ?
	if (document.location.toString().substr(0,4) == "file") {
		var path = document.location.toString();
		var localPath = getLocalPath(path);
		saveChanges();
	}
	// get original
	var uploadParams = Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
	var originalPath = document.location.toString();
	// If url is a directory : add index.html
	if (originalPath.charAt(originalPath.length-1) == "/")
		originalPath = originalPath + "index.html";
	var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
	var log = new bidix.UploadLog();
	log.startUpload(storeUrl, dest, uploadDir,  backupDir);
	displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
	if (bidix.debugMode) 
		alert("about to execute Http - GET on "+originalPath);
	var r = doHttp("GET",originalPath,null,null,null,null,callback,uploadParams,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

bidix.upload.uploadRss = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		if(status) {
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
			bidix.upload.uploadMain(params[0],params[1],params[2]);
		} else {
			displayMessage(bidix.upload.messages.rssFailed);			
		}
	};
	// do uploadRss
	if(config.options.chkGenerateAnRssFeed) {
		var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
		var rssUploadParams = Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
		bidix.upload.httpUpload(rssUploadParams,convertUnicodeToUTF8(generateRss()),callback,Array(uploadParams,original,posDiv));
	} else {
		bidix.upload.uploadMain(uploadParams,original,posDiv);
	}
};

bidix.upload.uploadMain = function(uploadParams,original,posDiv) 
{
	var callback = function(status,params,responseText,url,xhr) {
		var log = new bidix.UploadLog();
		if(status) {
			// if backupDir specified
			if ((params[3]) && (responseText.indexOf("backupfile:") > -1))  {
				var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
				displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
			}
			var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
			displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
			store.setDirty(false);
			log.endUpload("ok");
		} else {
			alert(bidix.upload.messages.mainFailed);
			displayMessage(bidix.upload.messages.mainFailed);
			log.endUpload("failed");			
		}
	};
	// do uploadMain
	var revised = bidix.upload.updateOriginal(original,posDiv);
	bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};

bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
	var localCallback = function(status,params,responseText,url,xhr) {
		url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
		if (xhr.status == httpStatus.NotFound)
			alert(bidix.upload.messages.storePhpNotFound.format([url]));
		if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
			alert(responseText);
			if (responseText.indexOf("Debug mode") >= 0 )
				responseText = responseText.substring(responseText.indexOf("\n\n")+2);
		} else if (responseText.charAt(0) != '0') 
			alert(responseText);
		if (responseText.charAt(0) != '0')
			status = null;
		callback(status,params,responseText,url,xhr);
	};
	// do httpUpload
	var boundary = "---------------------------"+"AaB03x";	
	var uploadFormName = "UploadPlugin";
	// compose headers data
	var sheader = "";
	sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
	sheader += uploadFormName +"\"\r\n\r\n";
	sheader += "backupDir="+uploadParams[3] +
				";user=" + uploadParams[4] +
				";password=" + uploadParams[5] +
				";uploaddir=" + uploadParams[2];
	if (bidix.debugMode)
		sheader += ";debug=1";
	sheader += ";;\r\n"; 
	sheader += "\r\n" + "--" + boundary + "\r\n";
	sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
	sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
	sheader += "Content-Length: " + data.length + "\r\n\r\n";
	// compose trailer data
	var strailer = new String();
	strailer = "\r\n--" + boundary + "--\r\n";
	data = sheader + data + strailer;
	if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
	var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
	if (typeof r == "string")
		displayMessage(r);
	return r;
};

// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
	if (!posDiv)
		posDiv = locateStoreArea(original);
	if((posDiv[0] == -1) || (posDiv[1] == -1)) {
		alert(config.messages.invalidFileError.format([localPath]));
		return;
	}
	var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
				store.allTiddlersAsHtml() + "\n" +
				original.substr(posDiv[1]);
	var newSiteTitle = getPageTitle().htmlEncode();
	revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
	revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
	revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
	revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
	revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
	return revised;
};

//
// UploadLog
// 
// config.options.chkUploadLog :
//		false : no logging
//		true : logging
// config.options.txtUploadLogMaxLine :
//		-1 : no limit
//      0 :  no Log lines but UploadLog is still in place
//		n :  the last n lines are only kept
//		NaN : no limit (-1)

bidix.UploadLog = function() {
	if (!config.options.chkUploadLog) 
		return; // this.tiddler = null
	this.tiddler = store.getTiddler("UploadLog");
	if (!this.tiddler) {
		this.tiddler = new Tiddler();
		this.tiddler.title = "UploadLog";
		this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
		this.tiddler.created = new Date();
		this.tiddler.modifier = config.options.txtUserName;
		this.tiddler.modified = new Date();
		store.addTiddler(this.tiddler);
	}
	return this;
};

bidix.UploadLog.prototype.addText = function(text) {
	if (!this.tiddler)
		return;
	// retrieve maxLine when we need it
	var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
	if (isNaN(maxLine))
		maxLine = -1;
	// add text
	if (maxLine != 0) 
		this.tiddler.text = this.tiddler.text + text;
	// Trunck to maxLine
	if (maxLine >= 0) {
		var textArray = this.tiddler.text.split('\n');
		if (textArray.length > maxLine + 1)
			textArray.splice(1,textArray.length-1-maxLine);
			this.tiddler.text = textArray.join('\n');		
	}
	// update tiddler fields
	this.tiddler.modifier = config.options.txtUserName;
	this.tiddler.modified = new Date();
	store.addTiddler(this.tiddler);
	// refresh and notifiy for immediate update
	story.refreshTiddler(this.tiddler.title);
	store.notify(this.tiddler.title, true);
};

bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir,  backupDir) {
	if (!this.tiddler)
		return;
	var now = new Date();
	var text = "\n| ";
	var filename = bidix.basename(document.location.toString());
	if (!filename) filename = '/';
	text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
	text += config.options.txtUserName + " | ";
	text += "[["+filename+"|"+location + "]] |";
	text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
	text += uploadDir + " | ";
	text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
	text += backupDir + " |";
	this.addText(text);
};

bidix.UploadLog.prototype.endUpload = function(status) {
	if (!this.tiddler)
		return;
	this.addText(" "+status+" |");
};

//
// Utilities
// 

bidix.checkPlugin = function(plugin, major, minor, revision) {
	var ext = version.extensions[plugin];
	if (!
		(ext  && 
			((ext.major > major) || 
			((ext.major == major) && (ext.minor > minor))  ||
			((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
			// write error in PluginManager
			if (pluginInfo)
				pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
			eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
	}
};

bidix.dirname = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(0, lastpos);
	} else {
		return filePath.substring(0, filePath.lastIndexOf("\\"));
	}
};

bidix.basename = function(filePath) {
	if (!filePath) 
		return;
	var lastpos;
	if ((lastpos = filePath.lastIndexOf("#")) != -1) 
		filePath = filePath.substring(0, lastpos);
	if ((lastpos = filePath.lastIndexOf("/")) != -1) {
		return filePath.substring(lastpos + 1);
	} else
		return filePath.substring(filePath.lastIndexOf("\\")+1);
};

bidix.initOption = function(name,value) {
	if (!config.options[name])
		config.options[name] = value;
};

//
// Initializations
//

// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);

// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");

//optionsDesc
merge(config.optionsDesc,{
	txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
	txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
	txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
	txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
	txtUploadUserName: "Upload Username",
	pasUploadPassword: "Upload Password",
	chkUploadLog: "do Logging in UploadLog (default: true)",
	txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});

// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');


/* don't want this for tiddlyspot sites

// Backstage
merge(config.tasks,{
	uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");

*/


//}}}


!Welcome to my TiddlyWiki

This TiddlyWiki is now pretty out of date. You should follow development at the googlecode project

[[My googlecode project|http://code.google.com/p/enderstremserver/]] 

If you would like to reach me, you may do so by email me at ender27182818 at gmail dot com. I generally answer emails very quickly. My email address is also a good way to suggest additions/changes to my code base. Or offer your help on coding.