ID:138833
 
Code:
    ProcessForm()
var/mob/creating_character/player = usr

var/ckey_name = ckey(name)
if (!ckey_name || ckey_name == "")
player.error_text = "Your name must have alpha-numeric characters in it!"
DisplayForm()
return

var/list/characters = usr.client.base_CharacterNames()
if (characters.Find(ckey_name))
player.error_text = "Name already taken." // Won't find ckey_name? ----------
DisplayForm()
return

// Everything is okay, so create the new mob based on the class they chose.
var/mob/new_mob
switch(class)
if ("Player") new_mob = new /mob/Player()

// Set the new mob's attributes.
new_mob.name = name
switch(gender)
if ("Male") new_mob.gender = MALE

// Log their client into the new mob.
usr.client.mob = new_mob

// And finally, blank out their web page since they don't need it now.
new_mob << browse(null, "window=NewCharacter")
return


Problem description:

I'm working on creating my game's login screen, and in doing so have implemented Deadron's Character Handling and the htmllib library to help ease the task. I quickly put up the code to see how it would function and after some tinkering, as well as stripping the options down to the bare minimum, I found that one can easily create a character with the same name, leading to potential disasters later on down the road. I've searched the forums for well over an hour and tried various fixes for this block of code, but still can't seem to come up with a solution.

This block of code is where the form for creating a new character is processed and I've tried creating a conditional that will catch the exception, which I commented. I've seen a similar fix in other people's code so I figured it would work here as well.

When I run this code and use a name that matches another one of my character names it ignores the exception and carries out the rest of the process normally, creating the new mob and overwriting the previous one.

I also looked into the library itself to find the process "base_CharacterNames()" just to get an idea of how it comes up with the list. From what I know, that would be the correct proc to use in order to check if a name is already taken.

Any suggestions as to go around solving this would be of great help. Thanks.
I personally have no idea how the base handling library works, but an easy way to fix this would be to check the players name against a global list of character names, and if it's not already in there add it in once the character's made.
In response to El Wookie
El Wookie wrote:
I personally have no idea how the base handling library works, but an easy way to fix this would be to check the players name against a global list of character names, and if it's not already in there add it in once the character's made.

I thought about doing that and figured it was a little more cumbersome compared to just checking it in that already made proc, but I suppose you're right. I'll have to try that later today and post my solution. Thanks, El Wookie.
In response to Bound
I've been thinking on how and where to construct a global list of character names and I figured, since it has to be kept in persistent storage, I'd create a savefile containing all the names of every player.

Code:
    ProcessForm()

var/mob/creating_character/player = usr

var/ckey_name = ckey(name)
if (!ckey_name || ckey_name == "")
player.error_text = "Your name must have alpha-numeric characters in it!"
DisplayForm()
return

var/FileName = "Players/AllNames.sav" // New code here ----------
var/savefile/F = new(FileName)
if (!ckey_name in CharNames)
F << ckey_name
else
player.error_text = "Name already taken."
DisplayForm()
return

// Everything is okay, so create the new mob based on the class they chose.
var/mob/new_mob
switch(class)
if ("Player") new_mob = new /mob/Player()

// Set the new mob's attributes.
new_mob.name = name
switch(gender)
if ("Male") new_mob.gender = MALE

// Log their client into the new mob.
usr.client.mob = new_mob

// And finally, blank out their web page since they don't need it now.
new_mob << browse(null, "window=NewCharacter")
return


world
New()

var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
if(fexists(FileName))
F["/"] >> CharNames
// else ..Do stuff, etc.

var/list/CharNames


I know it looks a little funky, but I'm just trying to get it into a working state first. Above world is the ProcessForm() proc that I mentioned earlier, and as you can see I've changed my conditional to check if ckey_name is in the list of character names (CharNames).

I believe the reason it's not working right now is because of the way I'm trying to load the savefile into CharNames. I've been fooling around with it for a bit, but it's still not working. What am I doing wrong and how might you go about fixing it? Thanks.
In response to Bound
You're slightly overcomplicating things there, the idea was indeed for you to use a savefile, but I personally would of made a
var/list/Char_Names = new/list()
Then add names to it on char creation. Then just load and save that list on world Del and New<.
In response to El Wookie
Ah, that makes a lot more sense. I totally forgot to even consider world.Del()!

Is the way I'm writing to and from the savefile into the list correct?

Code:
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
if(!ckey_name in Char_Names)
F << ckey_name


Such as the above code which is used during character creation?

I'm away from my computer with the code at the moment so I can't tinker with it, but I just wanted to make sure.
In response to Bound
if(!ckey_name in Char_Names)
Char_Names += ckey_name
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] << Char_Names


Loading would be similar, but using the <code>>></code> operator instead.
In response to El Wookie
Except I'm pretty sure if(!ckey_name in Char_Names) won't work. That's what the list.Find() proc is for
In response to Ill Im
var/list/CC = list("El Wookie")
mob/verb/ttt()
if(src.key in CC)
world<<1

This outputs 1 for me. <code>in</code> is a shorthand method for list.Find()
In response to Ill Im
However now that you mention it, it should be
if(!(ckey_name in CharNames))



=P
In response to El Wookie
Thanks for the help guys, but so far no luck. Here's what I have so far:

Code
ProcessForm()

var/mob/creating_character/player = usr

var/ckey_name = ckey(name)
if (!ckey_name || ckey_name == "")
player.error_text = "Your name must have alpha-numeric characters in it!"
DisplayForm()
return

if (!(ckey_name in Char_Names)) // Check if name is taken... ---------------
Char_Names += ckey_name
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] << Char_Names
else
player.error_text = "Name already taken."
DisplayForm()
return

// Everything is okay, so create the new mob based on the class they chose.
var/mob/new_mob
switch(class)
if ("Player") new_mob = new /mob/Player()

// Set the new mob's attributes.
new_mob.name = name
switch(gender)
if ("Male") new_mob.gender = MALE

// Log their client into the new mob.
usr.client.mob = new_mob

// And finally, blank out their web page since they don't need it now.
new_mob << browse(null, "window=NewCharacter")
return


world
New()
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] >> Char_Names

Del()
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] << Char_Names

var/list/Char_Names = new/list()


Still lets me login and overwrite the previous character... I believe the problem may lie in either the initialization of the list, or the loading of the list. Just can't figure it out. :/

[EDIT]
Just tried changing:

if(!(ckey_name in Char_Names))


to:

if(!ckey_name in Char_Names)


Unfortunately, it just denied every name I tried... Not sure if that'll help anyone though.
In response to Bound
Ok I've been tinkering with it for some time now... Was able to get a runtime error for all of my efforts.

What I did was instead of using:

if(!(ckey_name in Char_Names))
Char_Names += ckey_name
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] << Char_Names
else
player.error_text = "Name already taken."
DisplayForm()
return


I used:

if(!Char_Names.Find(ckey_name))
same code here


Tried creating a character with the same name as before and got:

runtime error: Cannot execute null.Find().
proc name: ProcessForm (/Form/NewCharacter/ProcessForm)
usr: Bound (/mob/creating_character)
src: Bound (/Form/NewCharacter)
call stack:
Bound (/Form/NewCharacter): ProcessForm()
Bound (/Form/NewCharacter): StopWaiting()
Bound (/Form/NewCharacter): Topic("src=%5B0x21000004%5D&name=Boun...", /list (/list))


Pressed submit twice and got:

Illegal form call by (Bound,/Form/NewCharacter).


Judging from the first line of the runtime error, I can conclude that the list hasn't been initialized just yet and it's trying to check the name inside of an empty list... That, or the list has been initialized, but the values haven't been read into it yet so the problem must be in the way it's being loaded or initialized. I might be wrong, but I feel the runtime error should help.
In response to Bound
Try adding in some if(fexists("Players/AllNames.sav")) checks, just to be sure the list is being initiated properly, then delete whatever AllNames.sav file is currently there. It may have some corrupt data in it from all the tweaks being made.
In response to El Wookie
Alright, I threw in the checks like you said in world.Del() and during character creation in order to overwrite the previous savefiles, but still to no avail.

I also found out through this post: here that the list is probably the problem. It may not be initialized properly or in the right place or has no data...

I also tried to initialize it in the ProcessForm() proc, but that's only a temporary list and wouldn't work.

Anymore ideas? Thanks for taking the time to help me.
In response to El Wookie
Sort of got it to work... Don't know why it does, though.

Here's the relevant code:

    ProcessForm()
var/mob/creating_character/player = usr

var/ckey_name = ckey(name)
if (!ckey_name || ckey_name == "")
player.error_text = "Your name must have alpha-numeric characters in it!"
DisplayForm()
return

if (!(ckey_name in Char_Names))
Char_Names += ckey_name
var/FileName = "Players/AllNames.sav"
if(fexists(FileName)) fdel(FileName)
var/savefile/F = new(FileName)
F["Names"] << Char_Names
else
player.error_text = "Name already taken."
DisplayForm()
return

// Everything is okay, so create the new mob based on the class they chose.
var/mob/new_mob
switch(class)
if ("Player") new_mob = new /mob/Player()

// Set the new mob's attributes.
new_mob.name = name
switch(gender)
if ("Male") new_mob.gender = MALE

// Log their client into the new mob.
usr.client.mob = new_mob

// And finally, blank out their web page since they don't need it now.
new_mob << browse(null, "window=NewCharacter")
return

world
New()
var/FileName = "Players/AllNames.sav"
var/savefile/F = new(FileName)
F["Names"] >> Char_Names

Del()
var/FileName = "Players/AllNames.sav"
if(fexists(FileName)) fdel(FileName)
var/savefile/F = new(FileName)
F["Names"] << Char_Names

var/list/Char_Names = new/list()


Surprisingly enough, I deleted the new in front of new(FileName) under world.New(), logged in, closed the world, put the new back and logged in again and everything worked just fine... How that made it work, I'll never know. If you could tell me, that'd be fantastic.

[EDIT]
Laughing at myself pretty hard right now... Figured out the reason why it never worked after I got it to what should have been a working state... Was missing this line in world.New() to check if the file existed in the first place (which it didn't):

if(fexists(FileName))

Note: Place the above line in world.New() after var/FileName = "Players/AllNames.sav"

So that whole time, I was creating a new, empty savefile and trying to load information from it because I didn't bother to check if it even existed. Now, for instance, if the game is created and no players exist, the savefile AllNames.sav won't exist and it won't try to load any names, and once the world is shutdown, it will create it and every time from then on the world will load the names in the savefile! Thanks for all the help, El Wookie! :)

Also one more thing, if you load the world for the first time and shut it down without any players being created, beware that the savefile will be created, but won't have any player names. This will result in the same runtime error whenever someone tries to create a player so make sure you create a player.

And to anyone who finds this thread helpful, here's how to go about with character deletion:

Topic(href, href_list[])
var/menu = href_list["menu"]
switch(menu)
if ("choosing_character")
// Close the menu window.
src << browse(null, "window=CharacterMenu")

var/choice = href_list["choice"]
ChooseCharacterResult(choice)
return

if ("deleting_character")
// Close the menu window.
src << browse(null, "window=CharacterMenu")

var/choice = href_list["choice"]
DeleteCharacterResult(choice)
Char_Names.Remove(choice) // Where the magic happens! -------
var/FileName = "Players/AllNames.sav"
if(fexists(FileName)) fdel(FileName)
var/savefile/F = new(FileName)
F["Names"] << Char_Names
return

return ..()
In response to Bound
There's some irrelivent programming in there, but alass that should work, however one thing should be amended or else cause huge lag spikes later down the line.


        for(var/O in Char_Names)
F["Names"] << Char_Names


This should just be

        F["Names"] << Char_Names





Also good work on remembering character deletion! I never even thought of that!
In response to El Wookie
Yeah I realized that, thanks. I accidentally left it in the post (I edited it). Thanks for all the help! :)

As for character deletion, I got that to work easily, but for some reason it stopped working randomly. Going to look into it...
In response to El Wookie
Stumped on character deletion... I used the code I posted in order to do so, but for some reason it's not working. I passed the link the player clicks as an argument to the remove proc for Char_Names, which should work in my opinion... It's not deleting anything. Any ideas?
In response to Bound
Try outputting the variable <code>choice</code> to make sure it's the player's key and not actually the player's mob. Also it would probably be best to do an
if(choice in CharNames)


Just incase ;)
In response to El Wookie
Forgot to use ckey... Duh! :)

I looked into the library and followed the flow of execution and realized at the very end it's ckey that makes the difference. :) Thanks for all the help, El Wookie! I owe you!

Topic(href, href_list[])
var/menu = href_list["menu"]
switch(menu)
if ("choosing_character")
// Close the menu window.
src << browse(null, "window=CharacterMenu")

var/choice = href_list["choice"]
ChooseCharacterResult(choice)
return

if ("deleting_character")
// Close the menu window.
src << browse(null, "window=CharacterMenu")

var/choice = href_list["choice"]
DeleteCharacterResult(choice)
Char_Names.Remove(ckey(choice)) // Where the magic happens! -------------
var/FileName = "Players/AllNames.sav"
if(fexists(FileName)) fdel(FileName)
var/savefile/F = new(FileName)
F["Names"] << Char_Names
return

return ..()
Page: 1 2