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.

