Game Development Community

Saving player profile/information woes. (SOLVED)

by Richard Preziosi · in Torque Game Engine · 10/12/2009 (10:42 pm) · 20 replies

What I'm trying to do is set my game up where when someone leaves a game it saves a bit of character information. Such as ammo/clips/weapons/name. All the things the player has collected and has not used during the game.

However I've yet to come across a decent save/load function, I've found many, but they all seem kinda vague, and usually end with, change what it saves to fit your needs!

This is a function that I want to use multiplayer, but this is not an mmo, so i feel like i could save clientside and just pass that players information to the server as the client is joining, but I'm not too sure on this being correct. I do have a web server if it would be easier to save character information to it through PHP, but that seems like even more work.

Is anyone kind enough to be a little more descriptive with a save/load function, using starter.fps that could save and load atleast one of the items described above. I would really appreciate it, this is just a bit too advanced for me, but something I want to implement.

Thanks

#1
10/13/2009 (3:20 am)
This really is an area that would be differant for everyone, while its nice to have the 'universal' resources, some really do depend on what you are trying to accomplish.

If you've found some save/load resources try those resources one by one and using lets say the weapon for your save/load test item until you've got what you consider good enough for the other items.

I myself have used the $prefs file for a "save/load" system while this is highly insecure, I have used it because it was the easiest for me too understand and control.

Theres many differant ways in which it can be done, but that just proves to show everyone will most likely not be using the same system.
#2
10/13/2009 (6:50 am)
preferences is secure enough for me, is it possible to have preferences stored clientside and still be effective if you join someone else's server?

I'd love to have account creation and then http stored character files that get downloaded to the server as people join, but this isn't an MMO and seems overkill for having certain weapons selected, using certain models etc.

The only way I can think to do it, and this is only for weapons, is to have the preferences be a function to set keybinds, kind of like

if ($pref::weapon1)
      moveMap.bindCmd(keyboard, "1", "commandToServer(\'use\',\"weapon1\");", "");

Is that sufficient for setting weapon/name/player model or am I just hoping it is easier than it really is going to be?
#3
10/13/2009 (7:16 am)
How I would do it is store all the character preferences in a file (and optionally encrypt it, if they were more achievements than preferences), then when a player joins a server, they send the encrypted charater data to the server, which decrypts it, and gives that client's avatar the appropriate equipment/appearance/whatever. Then when the client leaves, the server encrypts new info (if that information changed during the course of the game), and sends it to the client to be saved.

The keybind way you outlined is overkill - how about this:
function equipWeapon(%val)
{
   if(%val)
      commandToServer('use',$Prefs::myFavouriteWeapon);
}

moveMap.bindCmd(keyboard,"1","equipWeapon","");
Then set $Prefs::myFavouriteWeapon for each client. That's the simple way, I think, if you're not worried too much about security.
#4
10/13/2009 (8:16 am)
Trying to follow you Daniel, haven't slept in like 2 days though, so that may be the problem. Why was my way overkill?

How I was setting it up was similiar to this, but now I'm not sure. This is what I was going to put in gameConnection::createPlayer
if ($pref::Glock)
   {
       %player.setInventory(Glock,1);
       %player.setInventory(GlockAmmo,10);
       %player.mountImage(GlockImage,0);
       moveMap.bindCmd(keyboard, "1", "commandToServer(\'use\',\"Glock\");", "");
   }
   if ($pref::AK47)
   {
       %player.setInventory(AK47,1);
       %player.setInventory(AK47Ammo,10);
       %player.mountImage(AK47Image,0);
       moveMap.bindCmd(keyboard, "1", "commandToServer(\'use\',\"AK47\");", "");
   }
#5
10/13/2009 (10:26 am)
With daniels method you are actually using less script, but you have to make sure that the $pref is an actual weapon so you dont get stuck without one.

In your method you would still want to use a Fixed variable such as $pref::myweapon and setup the 'if' like this:
if ($pref::myweapon $= "AK47")   
{
...
}
else if ($pref::myweapon $= "Glock")
{
...
}
else 
{
... // Default/Emergency Weapon loadout if $pref::myweapon isnt set.
}
You may have more control over your weapon setup such as individual ammo amounts and etc..

I'd recommend using Daniels method for it being the least amount of code needed to make it work.(which would be the best performance wise)

However I'm not sure about setting the moveMap in your game.cs as this is a server file and may set every client to have the server's moveMap you have set.

A little bit more advanced but check this out also:
www.garagegames.com/community/forums/viewthread/99965
My code is setup differantly, but all the functions and code is still pretty much there for client specific weapons, hopefully it can help you as this is the exact post I learned how to setup 'all' my client specific info!
#6
10/13/2009 (6:37 pm)
Yes, GameConnection::createPlayer is definitely not the place to put move map commands. This function is called on the server when a client joins, and the server needs to create a character for them, etc. Binding commands to the server's moveMap will have unexpected effects (specifically: the host player will have all sorts of odd commands bound to their move map), and won't even affect the client at all. Also, you don't have access to a client's $pref variables on the server - all that code would refer to the server-side $pref variable, which probably means the game's host's prefs.

I didn't really mean 'overkill' as such, but I didn't think it was a good idea - I just couldn't put my finger on why. Binding commands inside an if clause doesn't seem right to me, that's just a personal feeling.
But the more important point is you're duplicating lots of code. If the "1" key is always going to activate a weapon, make it just call the 'activate weapon' function and sort out which weapon in that function.

I would structure it like this:
-Client joins game
-In GameConnection::createPlayer, the server creates a player for the client with no initial loadout
-When the client's initial control is set (forgotten the function called when this happens, look in the client directory scripts), they send a commandToServer with their preferences
-In the server command, the appropriate inventory limits are set and images mounted
#7
10/13/2009 (7:21 pm)
Yeah noticed this morning that my code doesn't work on a non hosting machine. Doesn't set the keybinds either.

I don't think what I'm understanding is the whole ($pref::myweapon $= "") part. The only inventory I've worked with is stock starter.fps, so there are no preferences there, and the only preferences involved in the demo are standard like, pref::hostmultiplayer. Looks like you guys have a preference category, i understand the usage, just don't know how you set it up everywhere else to make it work like that. is pref::myweapon being set by using pref::myweapon = "glock", if so then I'm starting to understand it.

Is this correct?:
function PrimaryWeapon()
if ($pref::myweapon $= "Glock")
   {
       commandToServer('use',"Glock");   
   }
   else if ($pref::myweapon $= "AK47")
   {
       commandToServer('use', "AK47");   
   }

Then set my config.cs, and default.bind.cs with this to map the weapons?:
moveMap.bindCmd(keyboard,"1","PrimaryWeapon","");

Going to try it and see if it works, just wanting to know if I'm starting to understand or not, or if i'm still doing it the less efficient way.

Also is the $= what is going to make this work multiplayer? and if so what is the reasoning behind that. Thanks a lot, been very helpful so far.

Edit:Hmm for some reason which makes no sense, the above code, not only doesn't work, but it makes my character spawn somewhere under the world and just fall forever. I don't think i'm grasping this.
#8
10/14/2009 (1:19 am)
Any errors in the console? That's very odd. Just to make sure, are you actually defining PrimaryWeapon like that? You should have another set of brackets after the function line, around the function body, and the function should also have a %val parameter (it will equal 1 when the key is pressed down, 0 when it's released). Also check to see if you left any changes in GameConnection::createPlayer - that's probably where things have been messed up.

Quote:is pref::myweapon being set by using pref::myweapon = "glock"
Yep, you got it. That just seemed to be the logical way, in my mind, rather than having a bunch of different flag prefs.

To give you a bit of background, variables prefaced with $ are global variables. That means they can be used by any function executed by an instance of Torque where the variable is defined. (So if I had an instance of Torque on a server machine and a client machine, I could define $var1 on the server, and use it in any function that was run on the server... but not on the client, since they're two different instances of Torque.)

Confusingly, the $= operator doesn't have anything to do with global variables - it's just checking if two strings are equal (== checks whether two numbers are equal).

The reason this works (or should work :P) in multiplayer is that you need to consider what functions are being run on the client, and which on the server. Each of these instances of Torque have different responsibilities, as well as different powers over the simulation.

On the lient, input events are handled, as is GUI stuff. The server doesn't need to handle GUIs because not every client needs to know about the state of every other client's GUI.
The server handles the actual world simulation - all the objects that exist, their movements, etcetera. All the clients do need to know about these things. Also, if something like a projectile position were handled on the client, it would open the game state to being hacked easily - because the client could just step in and say "well, yes, my projectile is actually right here... in front of your head..."

So the server, in this case, handles player creation, and inventory stuff. All those inventory scripts in starter.fps run on the server exclusively. All the client can do is tell the server 'the player has pushed the button to use weapon 1', and the server then figures out what action to take.
#9
10/14/2009 (3:09 am)
ok I forgot the extra brackets, i'm no longer below the world, but the weapons aren't being mounted, here is my updated code.

Game.cs
function GameConnection::createPlayer(%this, %spawnPoint)
{
   if (%this.player > 0)  {
      // The client should not have a player currently
      // assigned.  Assigning a new one could result in 
      // a player ghost.
      error( "Attempting to create an angus ghost!" );
   }

   // Create the player object
   %player = new Player() {
      dataBlock = PlayerBody;
      client = %this;
   };
   MissionCleanup.add(%player);

   // Player setup...
   %player.setTransform(%spawnPoint);
   %player.setShapeName(%this.name);
   
   // Starting equipment
   %player.setInventory(Glock,1);
   %player.setInventory(AK47,1);
   %player.setInventory(GlockAmmo,999);
   %player.setInventory(AK47Ammo,999);


   // Update the camera to start with the player
   %this.camera.setTransform(%player.getEyeTransform());

   // Give the client control of the player
   %this.player = %player;
   %this.setControlObject(%player);
}

function PrimaryWeapon()
{
   if ($pref::myweapon $= "Glock")
   
   {
       commandToServer('use',"Glock");
       %player.mountImage(GlockImage,0);   
   }
   else if ($pref::myweapon $= "AK47")
   {
       commandToServer('use', "AK47");   
       %player.mountImage(AK47Image,0);
   }
}

bind.defaults.cs and config.cs
moveMap.bindCmd(keyboard,"1","PrimaryWeapon","");

My gui element sets this in my prefs.cs
$pref::myweapon = "Glock";

I'm guessing it's not working because primaryweapon is not tied into the CreatePlayer function, I could be wrong about this, just not sure how to do it or I would try.

The only thing it says in the console log is parse error when I press the 1 key.
#10
10/14/2009 (4:57 am)
The PrimaryWeapon function needs to be in default.bind, where the other bound functions are.

Also, the code to mount images shouldn't be in PrimaryWeapon. This function is called by the client when they press "1", and remember the client doesn't have control over the world simulation. The actual image mounting needs to be in the 'use' server command - I'm not too familiar with the inventory system, so it may be in there already. Probably so.
#11
10/14/2009 (5:37 am)
Moving it to default.bind didn't affect anything. The only time I've been halfway close was in post #4, and infact I'm not sure it doesn't work multiplayer as i was testing on the same computer from the same executable. But I still doubt that it works.

I hate to ask, but any chance you can show some working code and in which files you put what. I'm mostly understanding what you're saying, just not sure I understand enough to properly implement it. I'm sure once i see it properly implemented it'll click.

I really appreciate the help so far, and thanks for going into so much detail in the last few posts. Oh and I was using mount image so that the weapon would already be out upon spawn, was goign to set up primaryWeapon and secondaryWeapon preferences, so that's what that part was about.
#12
10/15/2009 (6:24 pm)
Hmm, i finally understand why your original script works Daniel.

Typed into the consol: commandToServer('use',$pref::myweapon);

and sure enough it mounts whatever weapon is selected in the inventory gui.

I've gotten rid of the parse error in the console, but nothing is showing up yet when i press the key. Typing in the function name does nothing either.

I have tried the following functions, and none work.
function usePrimaryWeapon(%val)
{
   if (%val)  
      commandToServer('use',$pref::myweapon);
}


function usePrimaryWeapon(%val)
{
   if (%val)  
   {
      commandToServer('use',$pref::myweapon);
   }
}

I based those setups off of other functions i found inside of default.bind.cs, one is the same as yours above.

I've also tried setting up the keybind different ways, to no avail.

So obviously something is wrong with my function, since typing in the command to client works, anyone know what is wrong with my function?

Thanks.
#13
10/15/2009 (6:42 pm)
I should have mentioned this earlier - have you deleted config.cs? Every time the engine runs, it saves the moveMap, which is created n default.bind.cs, into config.cs. If there's an existing config.cs, however (say, from last time you ran the engine), it won't exec default.bind.cs at all, it'll just use what it finds in config.cs. So each time you make changes to default.bind, you need to delete config.cs (and .dso, just to be sure).

I *think* that should be your problem.

I would show you some working code, but I'm away from my dev computer - and I prefer to solve problems the hard way ;). It helps all of us learn.
#14
10/15/2009 (7:11 pm)
I was actually adding the command to config.cs as well. I tried deleting it, but no new config.cs is produced, and this actually causes a parse error with config.cs gone. I know I'm doig something stupid somewhere that is causing this to malfunction.

And thanks for helping the hard way, definitely some learning going on, though it appears when it comes to Torque, i'm in the special class.
#15
10/15/2009 (8:58 pm)
Where does the parse error happen? I reckon that indicates that something's wrong with default.bind.cs, and with no config to fall back on, something's going wrong.

Don't be ashamed of asking questions, we were all new once ;). I had my fair share of newbish questions, and I took a while to understand all this stuff... datablocks happened to be my stumbling point.
#16
10/15/2009 (9:51 pm)
The parse error happens when I press the button, but if I replace config.cs I get no errors what so ever, just nothing happens. Gonna repost my code as it stands now, maybe you'll spot something.

prefs.cs
$pref::myweapon = "Crossbow";

default.bind.cs
function usePrimaryWeapon(%val)
{
   if (%val)  
      commandToServer('use',$pref::myweapon);
}

moveMap.bindCmd(keyboard,"1","usePrimaryWeapon","");

config.cs
moveMap.bindCmd(keyboard,"1","usePrimaryWeapon","");

Like I said if i open the console and type: commandToServer('use',$pref::myweapon); it will equip the weapon that is set in prefs.cs. But typing: usePrimaryWeapon(); does nothing, so I am assuming my function is wrong somehow.

Thanks
#17
10/15/2009 (10:03 pm)
hey Rich -

sounds like it's time for some debugging.
try putting some echo() statements in the relevant places.

* put one inside usePrimaryWeapon(): print the value of %val & $pref::myWeapon. etc.

* put one in ServerCmdUse() to confirm that it's being called, etc.

simple echos will go a long was towards debugging almost any sort of problem.

#18
10/15/2009 (10:36 pm)
Quote:But typing: usePrimaryWeapon(); does nothing, so I am assuming my function is wrong somehow.
Remember that you defined usePrimaryWeapon with an argument %val. Entering usePrimaryWeapon(); you're not passing any value to that argument. Try usePrimaryWeapon(1); then %val will be 1 (or 'true'), and the if statement will execute.

But like Orion said, some cheap debugging would help here.
#19
10/16/2009 (5:33 am)
Ha I've and to some degree, we've, been making this harder than it really is, simply using this, with no function works perfectly, and works multiplayer as well

config.cs and default.bind.cs
moveMap.bindCmd(keyboard,"1", "commandToServer('use',$pref::myweapon);","");

I really appreciate all the help, I've learned a bit and more importantly you guys have restored my faith in this community, been on and off with the engine for a few years now, and the community around right now has been the most helpful yet.

Again thanks all, it is really appreciated.
#20
10/16/2009 (8:42 pm)
Oh... okay! Glad you got it working ;D.