is the continued development of the codebase formerly known as CircleMUD.
Download it here:tba MUD Download Patches, Snippets and Source
tbaMUD is an acronym for The Builder Academy Multi User Dungeon - And we use it to mean both the Academy for training, and the codebase that is made freely available to the community.
TbaMUD's vision is to provide the MUDding community a stable and functional codebase that includes an in-depth World and help files that makes it ready to be molded into a custom MUD by its coders and builders. We also provide multiple resources to allow for feedback, contribution, and the sharing of ideas within the MUDding community to ensure constant development and improvements.
TbaMUD is highly developed from the programming side, but highly UNdeveloped on the game-playing side. So, if you're looking for a huge MUD with billions of spells, skills, classes, and races, tbaMUD will disappoint you. TbaMUD still has only the 4 original Diku classes, the original spells, and the original skills. On the other hand, if you're looking for a highly stable, developed, organized, and well documented "blank slate" MUD on which you can put your OWN ideas for spells, skills, and classes, then tbaMUD is what what you are looking for.
TBA is a low stress, no deadline, training environment where anyone with motivation can work at their own pace to learn as much or as little as they desire about building on the tbaMUD codebase (formerly known as CircleMUD). TBA is a MUDding resource dedicated to improving the MUDding community.TBA has been operating since 2000 and has trained 1000's of builders.
Who wouldn't want a MUD on a thumdrive they can run from any computer!?!
Dare asked me about running tbaMUD on a thumb drive. I had never tried this so I did a bit of research and did not find a simple linux solution. There are a few websites that offer ubuntu desktop like penlinux.com but the ubuntu server install failed. I thought about VMWare, but I didn't want a complex solution so I fell back on my old faithful Cygwin.
The instructions are simple. Just go to www.cygwin.com and run their setup.exe. Install from Internet, choose your thumbdrive as the installation directory, leave default package directiory, leave default direct connection, and choose a mirror. When you get to the "select packages" section you do have to expand the devel group and add: "gcc-core C compiler" the "make: the GNU version of the make utility" and "subversion: a version control system" To add a package just click where it says "skip" and it will change to the current version number available.
You can rerun setup.exe at any time to add other packages like patchutils, etc.
Now open cygwin.bat from the thumbdrive, change directory to you thumbdrive (cd f: for me) and checkout the latest tbamud:
svn checkout http://tbamud.com/svn/circlemud/circlemud tbamud
(alternatively you can just download and uncompress tbamud-#.tgz from tbamud.com)
Checkout takes a while once complete:
cd tbamud
./configure
(wait for it, once complete)
cd src
make (q then enter to dismiss the license, wait for it again, once complete)
cd ..
bin/circle
Then point your MUD client of choice to localhost port 4000.
That is it. I was a bit disappointed with the speed. I haven't compiled a MUD that slowly in a decade, but it may just be my ancient thumbdrive. It is a sacrifice to be expected for portability.
Total time required: 2 hours (can you do better?)
It used 422MB on my 2GB thumb drive.
Please post in the comments below of your success, failures, and install time from thumb drive insert to tbaMUD login.
Download tbaMUD 3.61 or the patch at http://tbamud.com
More bug fixes, new areas command for players and an impoved idea, bug, typo command.
**WARNING** The new idea, bug, typo code will overwrite your existing idea, bug, and typo files. So make a backup or rename them if you add this code to your existing MUD.
The entire tbaMUD changelog: http://tbamud.com/files/changelog.txt
Changes this release:
tbaMUD 3.61
[Jan 27 2010] - Rumble
Updated World (thanks Parna) and other text files for release.
[Jan 18 2010] - Fizban
Edited scuba check so immortals with nohassle on don't need one.
[Jan 18 2010] - Rumble
Fixed bad AFF_SCUBA check for underwater rooms.
[Dec 28 2009] - Rumble
Fixed bug where an immortal could cast 'invis' and then be unable to break it with vis.
[Dec 25 2009] - Rumble
Added new mob flag NOKILL.
[Dec 23 2009] - Rumble
Added zone flags and min/max level to show zone # and stat zone #.
Changed show zone # reset mode from a # to the actual string description.
Changed Message Type on stat object from a number to the actual message type. i.e. pierce.
[Dec 22 2009] - Rumble
Made copyover save loadroom so players stay in the same room during copyover.
Added scan command.
[Dec 21 2009] - Rumble
Added identify command to shops.
Moved identify to an active spell. Cleric 11, Mage 20.
[Dec 17 2009] - Rumble
Standardized /n/r to /r/n in ibt.c
Removed ability to set a modifier to obj apply NONE.
[Dec 16 2009] - Rumble
Added some missing code for last_ibt in ibt.c.
Fixed typos in ibt.c (thanks Elervan)
[Dec 13 2009] - Rumble
Changed set password to level GRGOD.
[Dec 08 2009] - Rumble
Added "current zone:" to immortal score.
[Dec 07 2009] - Rumble
Renumbered UNUSED triggers to UNUSED# to make it easier to unset them when changing trig types. (thanks Parna)
Fixed double free in IBT that caused crash on /a or saving without any description.
[Dec 06 2009] - Rumble
Fixed medit autoroll so it would recognize a change and save.
[Dec 05 2009] - Rumble
Changed zedit clear level restrictions to dump you back at main zedit menu instead of in the level menu.
Changed zone level restriction to level recommendation so players would not be blocked from zones.
Changed Zcheck to level 31.
[Nov 19 2009] - Rumble
Fixed list obj max/min from room to obj. (thanks Elervan)
[Nov 13 2009] - Rumble
Removed con_app 2nd apply type shock that was for "resurrection?" This was not used in 3.1.
[Nov 12 2009] - Rumble
Fixed overflow in prefedit.c. (thanks Xiu)
[Nov 01 2009] - Rumble
Renamed autorun log entries from autoscript killed to terminated so it wouldn't go into the log/rip file.
[Nov 01 2009] - Fizban
Changed MOB_OR_IMPL macro to allow player-attachable scripts to work properly and edited the output of 'show zones'.
[Oct 27 2009] - Rumble
Added zone 555/556 qst files.
PLR_BUG/IDEA/TYPO flag cleanup.
[Oct 24 2009] - Jamdog
Added new IBT system for Ideas, Bugs and Typos (thanks Frenze)
**WARNING** This will create new files so backup your old ones first.
[Oct 23 2009] - Rumble
Fixed bug in renumbering zone table after deleting mobiles. (thanks Drefs)
Fixed bug in renumbering zone table after deleting objects. (thanks Drefs)
Upgraded column_list to use player screenwidth to determine how many columns to create (use column arg of 0). (thanks Maclir)
[Oct 20 2009] - Rumble
Corrected several PRF_FLAGGED checks that mobs were using.
Removed l-desc from stat player and title from stat mob. (thanks Xiu)
[Oct 19 2009] - Rumble
Added a note to spec_assign.c assign_mobiles that guildguard, snake, thief, magic user, puff, fido, janitor, and cityguards are now implemented via
triggers.
Fixed a bug where empty drink containers and fountains would show half empty. (thanks Parna)
[Oct 17 2009] - Rumble
Added Ultima Zone 555 and 556. Originally by Casret, rebuilt by Parna.
Made MEDIT column menu's consistent with other OLC menu's.
added TEDIT access to bugs, typos, and ideas file.
[Oct 09 2009] - Rumble
Added connected_type "Preference Edit" (thanks Maoliosa)
[Oct 08 2009] - Rumble
Fixed do_purge to allow targetting of multiple objects of the same name. i.e. 2.knife. (thanks Drefs)
[Oct 07 2009] - Rumble
Added CAN_SEE_IN_DARK and AFF_BLIND checks in do_automap. (thanks Frenze)
[Oct 06 2009] - Rumble
Fixed do_cast to allow targetting of multiple objects of the same name. i.e. 2.knife. (thanks Drefs)
[Oct 04 2009] - Rumble
Fixed a bug on filling unlimited containers where it doesn't update weight. (thanks Drefs)
[Oct 02 2009] - Rumble
Fixed buildwalk so it does not require the first room to exist. (thanks Zizazat)
Below is an example of how you can add/modify a mob flag. After initially creating a new NOKILL flag I changed my mind and instead renamed NOBASH to NOKILL and added checks to prevent attacking and spell casting. The only thing I left out was protection from triggers.
Index: fight.c
===================================================================
--- fight.c (revision 197)
+++ fight.c (working copy)
@@ -682,8 +682,9 @@
return (0);
}
- /* shopkeeper protection */
- if (!ok_damage_shopkeeper(ch, victim))
+ /* shopkeeper and MOB_NOKILL protection */
+ if (!ok_damage_shopkeeper(ch, victim) || MOB_FLAGGED(victim, MOB_NOKILL)) {
+ send_to_char(ch, "This mob is protected.\r\n");
return (0);
+ }
/* You can't damage an immortal! */
Index: structs.h
===================================================================
--- structs.h (revision 197)
+++ structs.h (working copy)
@@ -181,44 +181,44 @@
#define MOB_NOSUMMON 14 /**< Mob can't be summoned */
#define MOB_NOSLEEP 15 /**< Mob can't be slept */
-#define MOB_NOBASH 16 /**< Mob can't be bashed (e.g. trees) */
+#define MOB_NOKILL 16 /**< Mob can't be bashed (e.g. trees) */
#define MOB_NOBLIND 17 /**< Mob can't be blinded */
#define MOB_NOTDEADYET 18 /**< (R) Mob being extracted */
/** Total number of Mob Flags; it should be 1 less than MOB_NOT_DEADYET */
Index: constants.c
===================================================================
--- constants.c (revision 197)
+++ constants.c (working copy)
@@ -187,7 +187,7 @@
"NO_CHARM",
"NO_SUMMN",
"NO_SLEEP",
- "NO_BASH",
+ "NO_KILL",
"NO_BLIND",
"DEAD", /* You should never see this. */
"\n"
Index: spell_parser.c
===================================================================
--- spell_parser.c (revision 200)
+++ spell_parser.c (working copy)
@@ -212,6 +212,10 @@
act("White light from no particular source suddenly fills the room, then va
nishes.", FALSE, caster, 0, 0, TO_ROOM);
return (0);
}
+ if (cvict && MOB_FLAGGED(cvict, MOB_NOKILL)) {
+ send_to_char(caster, "This mob is protected.\r\n");
+ return (0);
+ }
/* determine the type of saving throw */
switch (casttype) {
case CAST_STAFF:
Index: act.offensive.c
===================================================================
--- act.offensive.c (revision 197)
+++ act.offensive.c (working copy)
@@ -294,12 +294,13 @@
send_to_char(ch, "Aren't we funny today...\r\n");
return;
}
+ if (MOB_FLAGGED(vict, MOB_NOKILL)) {
+ send_to_char(ch, "This mob is protected.\r\n");
+ return;
+ }
percent = rand_number(1, 101); /* 101% is a complete failure */
prob = GET_SKILL(ch, SKILL_BASH);
- if (MOB_FLAGGED(vict, MOB_NOBASH))
- percent = 101;
-
if (percent > prob) {
damage(ch, vict, 0, SKILL_BASH);
GET_POS(ch) = POS_SITTING; As discussed on the forums here is how to add identify:
Index: interpreter.c
===================================================================
--- interpreter.c (revision 197)
+++ interpreter.c (working copy)
@@ -171,6 +171,7 @@
{ "holylight", "holy" , POS_DEAD , do_gen_tog , LVL_IMMORT, SCMD_HOLYLIGHT },
{ "house" , "house" , POS_RESTING , do_house , 0, 0 },
+ { "identify" , "id" , POS_STANDING, do_not_here , 1, 0 },
{ "inventory", "i" , POS_DEAD , do_inventory, 0, 0 },
{ "idea" , "id" , POS_DEAD , do_ibt , 0, SCMD_IDEA },
{ "imotd" , "imo" , POS_DEAD , do_gen_ps , LVL_IMMORT, SCMD_IMOTD },
Index: spell_parser.c
===================================================================
--- spell_parser.c (revision 197)
+++ spell_parser.c (working copy)
@@ -916,7 +916,11 @@
TAR_CHAR_ROOM, FALSE, MAG_MANUAL,
NULL);
+ spello(SPELL_IDENTIFY, "identify", 50, 25, 5, POS_STANDING,
+ TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL,
+ NULL);
+
/* NON-castable spells should appear below here. */
spello(SPELL_IDENTIFY, "identify", 0, 0, 0, 0,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL,
Index: class.c
===================================================================
--- class.c (revision 197)
+++ class.c (working copy)
@@ -1586,6 +1586,7 @@
spell_level(SPELL_POISON, CLASS_MAGIC_USER, 14);
spell_level(SPELL_FIREBALL, CLASS_MAGIC_USER, 15);
spell_level(SPELL_CHARM, CLASS_MAGIC_USER, 16);
+ spell_level(SPELL_IDENTIFY, CLASS_MAGIC_USER, 20);
spell_level(SPELL_ENCHANT_WEAPON, CLASS_MAGIC_USER, 26);
spell_level(SPELL_CLONE, CLASS_MAGIC_USER, 30);
@@ -1607,6 +1608,7 @@
spell_level(SPELL_CURE_CRITIC, CLASS_CLERIC, 9);
spell_level(SPELL_SUMMON, CLASS_CLERIC, 10);
spell_level(SPELL_REMOVE_POISON, CLASS_CLERIC, 10);
+ spell_level(SPELL_IDENTIFY, CLASS_CLERIC, 11);
spell_level(SPELL_WORD_OF_RECALL, CLASS_CLERIC, 12);
spell_level(SPELL_EARTHQUAKE, CLASS_CLERIC, 12);
spell_level(SPELL_DISPEL_EVIL, CLASS_CLERIC, 14);
Index: shop.c
===================================================================
--- shop.c (revision 197)
+++ shop.c (working copy)
@@ -25,6 +25,7 @@
#include "constants.h"
#include "act.h"
#include "modify.h"
+#include "spells.h" /* for skill_name() */
/* Global variables definitions used externally */
/* Constant list for printing out who we sell to */
@@ -57,6 +58,7 @@
static int read_type_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max);
static int read_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max, int type);
static void shopping_list(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
+static bool shopping_identify(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static void shopping_value(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static void shopping_sell(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr);
static struct obj_data *get_selling_obj(struct char_data *ch, char *name, struct char_data *keeper, int shop_nr, int msg);
@@ -984,6 +986,8 @@
} else if (CMD_IS("list")) {
shopping_list(argument, ch, keeper, shop_nr);
return (TRUE);
+ } else if (CMD_IS("identify")) {
+ return (shopping_identify(argument, ch, keeper, shop_nr));
}
return (FALSE);
}
@@ -1543,3 +1547,114 @@
shop_index = NULL;
top_shop = -1;
}
+
+bool shopping_identify(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr)
+{
+ char buf[MAX_STRING_LENGTH];
+ struct obj_data *obj;
+ int i, found;
+
+ if (!is_ok(keeper, ch, shop_nr))
+ return FALSE;
+
+ if (SHOP_SORT(shop_nr) < IS_CARRYING_N(keeper))
+ sort_keeper_objs(keeper, shop_nr);
+
+ if (!*arg) {
+ snprintf(buf, sizeof(buf), "%s What do you want to identify??", GET_NAME(ch));
+ do_tell(keeper, buf, cmd_tell, 0);
+ return TRUE;
+ }
+ if (!(obj = get_purchase_obj(ch, arg, keeper, shop_nr, TRUE)))
+ return FALSE;
+
+ send_to_char(ch, "Name: %s\r\n", (obj->short_description) ? obj->short_description : "");
+ sprinttype(GET_OBJ_TYPE(obj), item_types, buf, sizeof(buf));
+ send_to_char(ch, "Type: %s\r\n", buf);
+ send_to_char(ch, "Weight: %d, Cost to Buy: @Y%d@n\r\n", GET_OBJ_WEIGHT(obj), sell_price(obj, shop_nr, keeper, ch));
+
+ sprintbitarray(GET_OBJ_WEAR(obj), wear_bits, TW_ARRAY_MAX, buf);
+ send_to_char(ch, "Can be worn on: %s\r\n", buf);
+
+ switch (GET_OBJ_TYPE(obj)) {
+ case ITEM_LIGHT:
+ if (GET_OBJ_VAL(obj, 2) == -1)
+ send_to_char(ch, "Hours Remaining: (Infinite)\r\n");
+ else if (GET_OBJ_VAL(obj, 2) == 0)
+ send_to_char(ch, "Hours Remaining: None!\r\n");
+ else
+ send_to_char(ch, "Hours Remaining: %d\r\n", GET_OBJ_VAL(obj, 2));
+ break;
+ case ITEM_SCROLL:
+ case ITEM_POTION:
+ send_to_char(ch, "Spells: %s, %s, %s\r\n",
+ skill_name(GET_OBJ_VAL(obj, 1)),
+ skill_name(GET_OBJ_VAL(obj, 2)),
+ skill_name(GET_OBJ_VAL(obj, 3)));
+ break;
+ case ITEM_WAND:
+ case ITEM_STAFF:
+ send_to_char(ch, "Spell: %s\r\n", skill_name(GET_OBJ_VAL(obj, 3)));
+ send_to_char(ch, "Charges: %d/%d\r\n", GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 1));
+ break;
+ case ITEM_WEAPON:
+ send_to_char(ch, "Damage Dice is '%dD%d' for an average per-round damage of %.1f.\r\n",
+ GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 2),
+ ((GET_OBJ_VAL(obj, 2) + 1) / 2.0) * GET_OBJ_VAL(obj, 1));
+ break;
+ case ITEM_ARMOR:
+ if(GET_OBJ_VAL(obj,1) == 0)
+ {
+ send_to_char(ch, "AC-apply: [%d]\r\n", GET_OBJ_VAL(obj, 0));
+ }
+ else
+ {
+ send_to_char(ch, "AC-apply: [%d] - This item has magical affects.\r\n", GET_OBJ_VAL(obj, 0));
+ }
+ break;
+ case ITEM_CONTAINER:
+ send_to_char(ch, "Capacity: %d/%d\r\n", GET_OBJ_WEIGHT(obj), GET_OBJ_VAL(obj, 0));
+ break;
+ case ITEM_DRINKCON:
+ case ITEM_FOUNTAIN:
+ send_to_char(ch, "Drinks: %d/%d\r\n", GET_OBJ_VAL(obj, 1), GET_OBJ_VAL(obj, 0));
+ break;
+ case ITEM_NOTE:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_KEY:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_FOOD:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_MONEY:
+ send_to_char(ch, "\r\n");
+ break;
+ case ITEM_WORN:
+ if(GET_OBJ_VAL(obj,1) > 0)
+ send_to_char(ch, "This item has magical affects.\r\n");
+ else
+ send_to_char(ch, "\r\n");
+ break;
+ default:
+ send_to_char(ch, "\r\n");
+ break;
+ }
+
+ found = 0;
+ send_to_char(ch, "Affections:");
+ for (i = 0; i < MAX_OBJ_AFFECT; i++)
+ if (obj->affected[i].modifier) {
+ sprinttype(obj->affected[i].location, apply_types, buf, sizeof(buf));
+ send_to_char(ch, "%s %+d to %s", found++ ? "," : "", obj->affected[i].modifier, buf);
+ }
+ if (!found)
+ send_to_char(ch, " None");
+
+ send_to_char(ch, "\r\nExtra Flags: ");
+ sprintbitarray(GET_OBJ_EXTRA(obj), extra_bits, EF_ARRAY_MAX, buf);
+ send_to_char(ch, "%s\r\n", buf);
+
+ return TRUE;
+}
Index: spells.h
===================================================================
--- spells.h (revision 197)
+++ spells.h (working copy)
@@ -89,6 +89,7 @@
#define SPELL_GROUP_RECALL 49 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_INFRAVISION 50 /* Reserved Skill[] DO NOT CHANGE */
#define SPELL_WATERWALK 51 /* Reserved Skill[] DO NOT CHANGE */
+#define SPELL_IDENTIFY 52 /* Reserved Skill[] DO NOT CHANGE */
/** Total Number of defined spells */
-#define NUM_SPELLS 51
+ #define NUM_SPELLS 52
@@ -113,7 +114,6 @@
* intended use is for spells and skills associated with objects (such as
* SPELL_IDENTIFY used with scrolls of identify) or non-players (such as NPC
* only spells). */
-#define SPELL_IDENTIFY 201
/* To make an affect induced by dg_affect look correct on 'stat' we need to
* define it with a 'spellname'. */ This question has come up a couple times lately so I decided to post it here to direct people as needed.
Players and mobs (NPC's) share many of the same data fields, but not all. So when a mob tries to access player data it gives a SYSERR like this:
SYSERR: Mob using >'((ch)-)player_specials-)saved.pref)' at act.wizard.c:127
The fix is actually a very easy one. All you have to do is make sure it only checks player data if it is a player. The way this is done in the code is with a non-player-character check. Now non-player-character (NPC) means it is a mob. So you need a double negative to make sure it is not a non-player-character. I know this is confusing, but just copy the example below.
- if (PRF_FLAGGED(ch, PRF_NOREPEAT)) // This line should be removed, hence the "-"
+ if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_NOREPEAT)) // This line should be added without the "+"
The changed line now will not just check for a flag, instead it will check if it is a player (not an NPC) and it is flagged then continue.
I hope this helps and please report these (and any other bugs/SYSERR's) you find. I've corrected dozens of these and they keep cropping up, especially when you switch into a mob and try player commands.
If you’re serious about developing a MUD, a version control system might just be your next best friend. Currently, tbaMUD uses SVN as part of their development platform, which fit right into what I was using to control my own source code! Although, I certainly appreciated the advantages of working with a version control system in tbaMUD and my custom source code, I ran into a number of gray areas. For me, that dangerous fog of war was the layout of my repository. In hopes that I can save some people a few steps, I'm going to lay out a repository structure that can maintain the developers custom source code and the public tbaMUD release versions.
Here's a quick check before we move on:
Creating a Repository
What backend should I use?
If you browse the subversion manual, you’ll see a big section dedicated to the Berkley DB and FSFS models for the repository backend. I wouldn’t put much time into contemplating this if you’re a beginner. Yet, It’s worth noting that by default Subversion began using FSFS in version 1.2. If your version is 1.1 or earlier (svnadmin -–version), I’d check into upgrading into the latest stable release. So....you'll be using an FSFS backend by default, which in most cases will work just fine.
svnadmin create $HOME/devrepos
In the sample above I created a repository named devrepos in my home directory, pretty painless.
At this point, It’s tempting to jump straight into importing a source copy into your new repository, but I strongly suggest you check the SVN config options first. After creating your repository you should have a folder named .subversion. Inside this directory you’ll find a file named config, which has a number of options you can set. You should carefully look over the entire file, but I’ll hit on three so we get an idea of the configurable options available.
global-ignores There are a few different ways to ignore files in subversion. However, this ignore option is special because of the global scope value attached to it (There is also a subversion global-ignores for the system, but any values set in your local home subversion config file will take precedence).
global-ignores = *.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo
*.rej *~ #*# .#* .*.swp .DS_Store *.plr *.objs
In the above sample I’ve added .plr and .objs to be globally ignored. Don’t get carried away here and begin adding uncommon/infrequent data types, specific file 'ignores' can be accomplished faster and easier using commands in the SVN client.
commit-times The commit-times will add some overhead in your repository, but it certainly helps if you butt heads with another user that’s committed a revision from an outdated copy of the source (surely you wouldn't make the same mistake).
Properties SVN properties are best described as being subversioned metadata. SVN uses a number of different property types, which can result in the management of a repository that much easier. On the other hand, it can be a slight nuisance having this active during the initial import of your code. I set up some additional values for the world files.
*.mob = svn:eol-style=native
*.obj = svn:eol-style=native
*.qst = svn:eol-style=native
*.shp = svn:eol-style=native
*.trg = svn:eol-style=native
*.wld = svn:eol-style=native
*.zon = svn:eol-style=native
During the import I received the following error:
svn: File '/home/brooks/importme/tbamud-3.59/lib/world/wld/284.wld' has inconsistent newlines
This resulted in the failure of my import because of wld file 284 in the 3.59 release. The property svn:eol-style=native tells subversion the EOL marker on the file is native to the system it was run on, file 284.wld disagreed.
In short, properties aren’t necessary for your repository to function, but they’re certainly worth examining. So you might wonder why I focused on this particular option in the first place? Subversion checks auto-property settings when a new file is imported or added. Setting a property on a file already in the repository requires it to be done manually (or with a script). Thus, don’t be surprised if you skip enabling this during import, only to find out that enabling it later doesn't automatically populate your file system with SVN properties.
Directory Structure Layout:
I'll make this part short and sweet. The correct answer is, whatever best fits the scope of your project. Directories names have absolutely no baring on how the repository will treat them. Hold on though, unofficially, you'll see a good number of repositories set up like so:
In this layout we would import our main source code into the trunk directory. If a new project or test source code was being developed, a copy from the trunk is brought into a new branch. Finally, tags make life easier by taking snap shots of specific revisions during the source code distribution life-cycle (okay, doubt we'll be distributing our custom source code, but don't ignore making a tag here and there).
FINALLY! We get to the good part!
Alright, what I'm going to demonstrate is how you can manage your own custom source code, while also managing (with lots of help from Mr. subversion) new/modified source code additions from the tbaMUD releases. What we're talking about is essentially patching in new source code with a utility that has a pretty damn good idea of how it should be arranged. Okay, let's start!
My directory structure:
brooks@lard:~$ ls
backup bin tartmp importme
Okay let's setup that new repository.
svnadmin create devrepos
Change into tartmp and grab the previous & current release versions of tbaMUD.
wget http://www.tbamud.com/Oasis_DG_pages/downloads/patches/releases/tbamud-3... \
http://www.tbamud.com/Oasis_DG_pages/downloads/patches/releases/tbamud-3...
Okay, time to slow down for a minute and explain what we're doing and how things will be laid out. In this example my custom code base will be named redsun. My source, redsun, will be using the tbamud-3.59 release. I've decided to use the "unofficial standard" directory layout for my repository.
Obviously, the public tbaMUD release is not part of our project, but we still want to patch in the updated source into our project. In the Subversion book a completely separate project branch is created, which they label 'vendor'. However, my project is fairly small and I don't want to spread my repository directories out. Thus, I'll be adding a tbamud directory into the branches root. Each version I add in the branches/tbamud branch will be created as a tag (I want to keep my tags directory open for my own source code snap shots). With that tidbit, we're off punching keys again!
Change into the importme directory and unpack version 3.59 (The version redsun will be using).
tar -zxvf ../tartmp/tbamud-3.59.tgz
Now we're going to import into the stock tbamud branch. Notice that the Subversion import command recursively recreates our path and any directories not already in the repository? Make sure you run this command inside of the importme directory, which should only contain the tbamud-3.59 source code directory.
svn import $HOME/importme/tbamud-3.59 \
file:///$HOME/devrepos/redsun/branches/tbamud/current \
-m 'importing stock tbamud-3.59 drop'
Now we're going to tag the current version as 3.59 for the merge performed down the road.
svn copy file:///$HOME/devrepos/redsun/branches/tbamud/current \
file:///$HOME/devrepos/redsun/branches/tbamud/3.59 \
-m 'tagging tbamud version 3.59'
Finally, copy the current tbamud version into our main branch. This is the custom source code that's now "branched" off from the tbaMUD project (Yea, if it looks like we're doing things backwards, we are! Just keep going).
svn copy file:///$HOME/devrepos/redsun/branches/tbamud/3.59 \
file:///$HOME/devrepos/redsun/trunk \
-m 'say first. first. redsun LIVES!'
Okay, a few months go by and what do you know?!? tbaMUD version 3.60 has arrived. Of course we want the additions, we just need to manage a way of patching it into our existing source code. Before we worry about that though, let's grab the 3.60 version and import it into our repository.
This part use to be a bit of a mess, fortunately, someone developed a hand dandy little script named svn_load_dirs.py to automate the process. Because of licensing issues, you might not have this script under /svn/tools. If a subsequent find brings up nothing, just download the script svn_load_dirs.py.in and rename it svn_load_dirs.py.
Okay we need to make a slight modification to the script. You'll want to figure out where your svn is (which svn) and than use an editor to change the following value in the script:
my $svn = '@SVN_BINDIR@/svn';
You'll probably end up with something like this:
my $svn = '/usr/bin/svn';
Now move the svn_load_dirs.py into the importme directory and change into the directory when ready for the next step!
Unpack the tbamud-3.60 tar
tar -zxvf ../tartmp/tbamud-3.60.tgz
Run the svn_load_dirs script to automate a merge between version 3.59 and 3.60. The script will move the 3.60 into the current directory structure and make a new tag named 3.60.
./svn_load_dirs.pl file:///$HOME/devrepos/redsun/branches/tbamud current \
tbamud-3.60 -t 3.60
Remove tbamud-3.60 and checkout a local copy of the redsun source code. We'll name the working source copy redmoon
svn co file:///$HOME/devrepos/redsun/trunk \redmoon
Okay, here comes the make it or break it command!
Change into the directory redmoon. You'll now perform a merge of the changes between 3.59 and 3.60 on your source code.
svn merge file:///$HOME/devrepos/redsun/branches/tbamud/3.59 \
file:///$HOME/devrepos/redsun/branches/tbamud/current .
Chances are, you'll run into a few conflicts that you'll need to resolve. A diff tool is available to resolve these conflicts if you should run into such a problem. After resolving any conflicts, DON'T FORGET TO COMMIT YOUR CHANGES back into the repository!
Okay that's it! Your changes are merged and your good to get back into the source code. The only thing I'd suggest is running a backup using the svnadmin hotcopy command.
I hope you didn't get lost along the way, as we did fly through the commands towards the end, but if you did...
Check out the Vendor Branches over at the svnbook page.
Here is another great source as well, it's an excellent video tutorial covering this same topic.
That concludes things for me, hope you you learned something new. At any rate, if you should decide to go fourth and make your own SVN repository, best of luck!
Download tbaMUD 3.6 or the patch at http://tbamud.com
More bug fixes, new prefedit and all the auto options including the new autodoor. Thanks again to Jamdog for the majority of the work this release! For the full tbaMUD changelog check out: http://www.tbamud.com/files/changelog.txt