Developer Store
Support
Member Forums

Screenshots
FAQ
Documentation
License
Known Issues
Downloads

MMOWorkshop.com Store Opened!
Torque MMO Kit - Open Sourced!
Torque MMO Kit - 1.5.2 Port Alpha Test
Torque MMO Kit - OSX Status

GarageGames.com irc.prairiegames.com
#mmoworkshop

PyTorque
TGB Web Browser


chayfo

hallsofvalhalla - After a long epiphany
Leathel - FoHO pre-Alpha 2.42
OldRod - More Musings on the MMO Industry
xapken - nice
J.C. Smith - 0.0.4.1 Build Notes
Wolf Dreamer - Pointless blog of pointless things
AthlonJedi2 - Server Nuked !!!!!
gamer_goof - New character model GIRL1 available only $4
... MORE BLOGS!

My other project
A Message to all the new people.
MyGame
changing the primary class
See Ya Classes, Hello Skills!
XP Based Skills
fxFoliageReplicator zone loading...
Is it just me, or is it slow in ...
Places where NPC quest-givers ha...
TalentRaspel Grid

These changes affect MULTIPLAYER ONLY.

What this tutorial does is show you the necessary changes that need to be made to create per character starting locations. It also shows the changes that need to be made to save the character’s location. No more level one players starting in a level 50 zone where your high toon logged. And finally, we update bind points to be set to each character as well. Currently this doesn’t save the character’s rotation.

Good Advice: Make a copy of your MUD folder and put it somewhere incase things go boom during your modifications. This will give you a fresh base to start over with.

Database Changes

This MUST BE DONE FIRST: Delete ALL characters from your server database. Either log on and delete each character; or, execute a delete statement from SQLAnalyzer against the server database.

The second thing that needs to be done is modifications to a few tables. This MUST BE DONE SECOND! This will create the needed fields for storing our zone id and transforms.

Open /mud/characterserver/serverdb.py and locate the following area of

Code:

CREATE_PLAYER_BUFFER_SQL = """ 
CREATE TABLE player_buffer 
( 
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    public_name TEXT, 
    buffer BLOB 
); 

CREATE TABLE character_buffer 
( 
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    public_name TEXT, 
    character_name TEXT UNIQUE, 
    race TEXT, 
    pclass TEXT, 
    sclass TEXT, 
    tclass TEXT, 
    plevel INTEGER, 
    slevel INTEGER, 
    tlevel INTEGER, 
    realm INTEGER, 
    rename INTEGER, 
    buffer BLOB 
); 

Change the character_buffer table to look like the following:

Code:

CREATE TABLE character_buffer 
( 
    id INTEGER PRIMARY KEY AUTOINCREMENT, 
    public_name TEXT, 
    character_name TEXT UNIQUE, 
    race TEXT, 
    pclass TEXT, 
    sclass TEXT, 
    tclass TEXT, 
    plevel INTEGER, 
    slevel INTEGER, 
    tlevel INTEGER, 
    realm INTEGER, 
    rename INTEGER, 
    logZone  integer references Zone(id), 
    logTransformInternal TEXT, 
    bindZone  integer references Zone(id), 
    bindTransformInternal TEXT, 
    buffer BLOB 
); 

Next, open up /mud/world/spawn.py

Add the following somewhere in your spawn class. I added comments so that I could find my changes easily if things didn’t go according to plan.

Code:

#EQ STARTING LOCATION KLF 
#all characters are bound to the same fate, er bindpoint/log point 
#not anymore Prairie Games :D 
bindTransformInternal = StringCol(default="") 
bindZone = ForeignKey('Zone',default=None) 
logTransformInternal = StringCol(default="") 
logZone = ForeignKey('Zone',default=None) 

Save your changes. Then run genesis and rebuild your character database. This is important as it rebuilds the spawn table. The character buffer will have the new fields when it gets built as well.

Code Changes

Since we are working with the database open up the following: Mud/characterserver/serverdb.py

Locate your getCharacterInfos function and add the following change:

Code:

cursor.execute("SELECT character_name,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,logZone,logTransformInternal,bindZone,bindTransformInternal FROM character_buffer WHERE public_name = '%s';"%publicName)


Make sure your select looks exactly the same. Order of the fields is important here.

Next in this file is the insertCharacterBuffer. Change the lines following buffer = to the following:

#EQ START ZONE 
buffer = sqlite.Binary(buffer) 
values = (None,publicName,characterName,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,logZone,logTransform,bindZone,bindTransform,buffer) 
#values = (None,publicName,characterName,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,buffer) 
cursor.executemany("INSERT INTO character_buffer VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",(values,)) 
cursor.close()


The original values line was commented out for reference. Done with that file. Save it and close it.

Next is /mud/world/cserveravatar.py

We need to modify the extractLoggingPlayer function. This is what saves your character’s data when you camp out.

What we are going to do is modify the CVALUES with our location in the world. The current CVALUES is pulling our info from the database which hasn’t been updated yet. I found this to be the easiest method (though maybe not the best) solution. Code:

publicName,pbuffer,cbuffer,cvalues = ExtractPlayer(player.publicName,player.id,player.party.members[0].id,False)


Add the following lines after the above line Code:

#create loc and bind strings from the tranform list #EQ START 
logLoc = str(player.logTransform[0]) + " " + str(player.logTransform[1]) + " " + str(player.logTransform[2]) + " " + str(player.logTransform[3]) + " " + str(player.logTransform[4]) + " " + str(player.logTransform[5]) + " " + str(player.logTransform[6]) 

bindLoc = str(player.bindTransform[0]) + " " + str(player.bindTransform[1]) + " " + str(player.bindTransform[2]) + " " + str(player.bindTransform[3]) + " " + str(player.bindTransform[4]) + " " + str(player.bindTransform[5]) + " " + str(player.bindTransform[6]) 
        
cvalues = c.name,c.spawn.race,c.spawn.pclassInternal,c.spawn.sclassInternal,c.spawn.tclassInternal,c.spawn.plevel,c.spawn.slevel,c.spawn.tlevel,c.spawn.realm,player.logZone.id,logLoc,c.spawn.bindZone.id,bindLoc


Now we are updating our character’s zone info. We are done here. Go ahead and save the file then close it.

Next up is /mud/world/playeravatar.py

There are many changes that need to be made here as this file manages the new character creation as well as passing the character off to the world.

Look for the following bit of code in newCharacter:

#fix this BS, score should be coming from world server and only adj being sent 
spawn.strBase = newchar.scores['STR'] + newchar.adjs['STR'] 
spawn.dexBase = newchar.scores['DEX'] + newchar.adjs['DEX'] 
spawn.refBase = newchar.scores['REF'] + newchar.adjs['REF'] 
spawn.agiBase = newchar.scores['AGI'] + newchar.adjs['AGI'] 
spawn.wisBase = newchar.scores['WIS'] + newchar.adjs['WIS'] 
spawn.bdyBase = newchar.scores['BDY'] + newchar.adjs['BDY'] 
spawn.mndBase = newchar.scores['MND'] + newchar.adjs['MND'] 
spawn.mysBase = newchar.scores['MYS'] + newchar.adjs['MYS']

Then below this piece of code, you want to add your code for setting your starting location and bind point. Here is a sample from my world (locations based on race/class combo):

Code:

if (newchar.race == "Human") or (newchar.race == "Halfelf"): 
         if newchar.klass == "Warrior": 
                zone = Zone.byName("alhanas") 
                spawn.logZone = zone 
                spawn.bindZone = zone 
               spawn.logTransformInternal = "113.11 -175.40 108.65 0 0 0 5" 
             spawn.bindTransformInternal = "113.11 -175.40 108.65 0 0 0 45" 
          elif newchar.klass == "Paladin": 
                zone = Zone.byName("alhanas") 
                spawn.logZone = zone 
                spawn.bindZone = zone 
               spawn.logTransformInternal = "57.40 -254.91 104.22 0 0 0 183" 
             spawn.bindTransformInternal = "57.40 -254.91 104.22 0 0 0 183" 

You will need to do this for all combos you are basing your starting areas for.

Onto gotCharacterInfos (we are still in playeravatar.py) Modify this section of code to look like this:

Code:

#EQ START ZNE 
names = [] 
for cname,cvalues in result.iteritems(): 
           names.append(cname) 
            name,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,logZone,logTransform,bindZone,bindTransform = cvalues 
            cinfo = CharacterInfo() 
            cinfo.status = "Alive" 
            cinfo.name = str(cname)            
            cinfo.race = str(race) 
            cinfo.realm = realm 
            cinfo.klasses.append(str(pclass)) 
            cinfo.levels.append(plevel) 
            cinfo.newCharacter = False 
            cinfo.rename = rename 
            cinfo.logZone = logZone 
            cinfo.logTransform = logTransform 
            cinfo.bindZone = bindZone 
            cinfo.bindTransform = bindTransform 
            cinfos.append(cinfo) 


The key additions are for the logZone, logTransform, bindZone, and bindTranform.

As a self check for all of this, I added various log files and spit out information for review. I did this to follow the logic through all of my changes. Here is a sample:

eq = open("./log_EQStart.txt","a") 
eq.write("\nplayeravatar.gotCharacterInfos-CINFOS %s\n”%(cinfo.logZone)) 
eq.close() 

We are going to modify the perspective_enterWorld function:

Locate the following lines:

Code:

if c.realm == RPG_REALM_DARKNESS: 
     zone = self.player.darknessLogZone.name 
elif c.realm == RPG_REALM_MONSTER: 
     zone = self.player.monsterLogZone.name 
elif c.realm == RPG_REALM_LIGHT: 

HERE IS WHERE THE CHANGES GO for light Please NOTE: If you are using MoM realms, you will need to modify the appropriate section.

Add the following lines. We are retrieving a zone object from the database based on the information we set when the character was created in case of a new character or from the database for a stored character.

Code:

filename = "data/character/character.db" 
conn = sqlite.connect(filename,isolation_level=None) 
cursor = conn.cursor() 
logzone = None 
cursor.execute("SELECT * FROM zone WHERE id = %s;"%(c.logZone)) 
logzone = cursor.fetchone() 
                        
#EQ START...Using the player's logzone stuff since it is global 
#but, the info comes from CHARACTERINFOs which is tied to an idividual 
#character. ALso, this server's MAXPARTY will be 1 
self.player.logZone = logzone[0] 
self.player.logTransformInternal = c.logTransform 
#Do the same for the character's bind zone in case he dies while AFK 
cursor.execute("SELECT * FROM zone WHERE id = %s;"%(c.bindZone)) 
logzone = cursor.fetchone() 
self.player.bindZone = logzone[0] 
self.player.bindTransformInternal = c.bindTransform 
zone = logzone[4] 
cursor.close() 
conn.close() 

Locate your enterWorld function and then find the ALL DEAD if block. Change your else statements (light character) to look like the following:

Code:

self.player.curChar.spawn.logTransformInternal = self.player.curChar.spawn.bindTransformInternal 

self.player.curChar.spawn.logZone = self.player.curChar.spawn.bindZone 

That’s it for playeravatar.py. Save your changes.

Okay open your mud/world/shared/worlddata.py file as we have changes to make here.

We are going to be adding lines to the CharacterInfos? class which is used to move data between the server and the database and (not sure here) the client.

Add the following lines after self.status = “”

Code:

self.logZone = None 
self.logTransformInternal = ""        #EQ START ZONE 
self.bindZone = None 
self.bindTransformInternal = "" 

scroll down a bit and add the following lines of code after the tclassinternal if block

Code:

self.logZone = char.spawn.logZone 
self.logTransformInternal = char.spawn.logTransformInternal 
self.bindZone = char.spawn.bindZone 
self.bindTransformInternal = char.spawn.bindTransformInternal 

The beginning of your class defines should now look something like this:

Code:

class CharacterInfo(pb.Copyable,pb.RemoteCopy): 
    def __init__(self,char=None):      
        self.name = "" 
        self.klasses = [] 
        self.levels = [] 
        self.race = "" 
        self.realm = RPG_REALM_NEUTRAL 
        self.rename = 0 
        self.status = "" 
        self.logZone = None 
        self.logTransformInternal = ""        #EQ START ZONE 
        self.bindZone = None 
        self.bindTransformInternal = "" 
    
        
        if char: 
            if char.dead: 
                self.status = "Dead" 
            else: 
                self.status = "Alive" 
                
            self.name = char.name 
            self.race = char.spawn.race 
            self.realm = char.spawn.realm 
            self.klasses.append(char.spawn.pclassInternal) 
            self.levels.append(char.spawn.plevel) 
            if char.spawn.sclassInternal: 
                self.klasses.append(char.spawn.sclassInternal) 
                self.levels.append(char.spawn.slevel) 
            if char.spawn.tclassInternal: 
                self.klasses.append(char.spawn.tclassInternal) 
                self.levels.append(char.spawn.tlevel) 
            self.logZone = char.spawn.logZone 
            self.logTransformInternal = char.spawn.logTransformInternal 
            self.bindZone = char.spawn.bindZone 
      self.bindTransformInternal = char.spawn.bindTransformInternal 

Now we need to change our /mud/characterserver/upgrade.py file.

Modify your values unpack string in UpgradeCharacterBuffer? to look like the following (it is the second line):

Code:

id,publicName,characterName,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,logZone,logTransform,bindZone,bindTransform,buffer = values 


This gets our new buffer fields some data. Scroll down a ways until you see the following line:

Code:

v = (None,publicName,characterName,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,buffer) 

Modify it to look like this:

Code:

v = (None,publicName,name,race,pclass,sclass,tclass,plevel,slevel,tlevel,realm,rename,logZone,logTransform,bindZone,bindTransform,buffer) 


Next modify your execute line by adding 4 more ‘?’ so that it looks like this (should have 17 ‘?’):

Code:

cursor.executemany("INSERT INTO character_buffer VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",(v,)) 

Lastly, we need to change the /mud/worldserver/charutil.py file: if you search for "#spawn info" (around line 544) you will see

Code:

cursor.execute("SELECT name,race,pclass_internal,sclass_internal,tclass_internal,plevel,slevel,tlevel,realm FROM spawn WHERE character_id = '%i' LIMIT 1;"%cid)

it needs to be changed to:

Code:

cursor.execute("SELECT name,race,pclass_internal,sclass_internal,tclass_internal,plevel,slevel,tlevel,realm,log_zone_id,log_transform_internal,bind_zone_id,bind_transform_internal FROM spawn WHERE character_id = '%i' LIMIT 1;"%cid)

TESTS
Start up your server, create a character and log in. You should start where you specified in the designated zone. Go to a bind spot and run your /bind command. Then run /suicide. You should reappear at your bind location. And for the final test, log out then re-connect as soon as you can. Create a new character with a different starting location. That character should appear there. Log him off, then re-log and choose your original character. You should be where you logged off.