Game Development Community

Accessing NetObjects from both Client and Server

by Bryan Edds · in Technical Issues · 08/26/2004 (2:04 pm) · 17 replies

Okay, I'm wondering if I can access a NetObject (or a derivative) from both the Client and the Server by doing this -

Let's say the object was created in the Client script like this -

new Player(BryanEdds)
{
// blah
};

Will the Server be able to access this object in script by specifying it's name like this -

BryanEdds.getVelocity();

I tried testing this, but unfortunately, my test bed is a bit... well... incomplete, and I'm not really sure if I'm getting results that are accurate to my question. So if anyone knows this off hand, I would really appreciate your response :)

Thank you.

#1
08/26/2004 (3:50 pm)
Yes, if BryanEdds is defined under the correct class, then you could use any "BrianEdds.foo()" function that applied to that class.
#2
08/26/2004 (4:30 pm)
It's not really an issue of classes, but rather the Server accessing the _instance_ of BryanEdds that was instantiated with new on the Client.

Although, you may be right on and I may have misunderstood your understanding of the context of my question. If that's so, then I apologize, and humbly ask for you to explain in a bit more detail. :)
#3
08/26/2004 (5:23 pm)
Quote:It's not really an issue of classes

Yes it is. Try to get the transform of a client and see what happens. Try to get the velocity on a static. It wont work. What class you define "BryanEdds" under will determine objects function base will be. Of course you can run it under pretty much ANY class if you are going to call the namespace directly with all scripted functions.

But if you intend to use some of the built in engine functions as well as your own, you will need to put "BryanEdds" under the best class to suit your needs.
#4
08/26/2004 (5:38 pm)
Uh, actually, I think it's an issue of scope. As in, if a NetObject _instance_ is declared in a Server, does the Client have access to that specific NetObject instance through it's object name (in this case, BryanEdds).

As far as I understand, a variable or object instance, or a function declared in a Server code is not available to the Client. Had this mutual exclusion of scope not been present, then I could easily make a conclusion. But since I believe this to be the case, I don't understand your answer. Now, if you can show me that this belief is incorrect or irrelevant to my problem, then I think I will be able to better understand your answer, and thus be able to put my question in better form.

Thank you for your help so far!
#5
08/26/2004 (6:41 pm)
Hmmmm, maybe if I understood better exactly what you are trying to accomplish, I could give you a better answer or even the correct solution.


Are you saying these are your steps?...

1. Bryan Edds logs into server

2. Server creates a new Net_Object named "Bryan_Edds"

3. Bryan Edds wants to get the velocity of "Bryan_Edds"



Now, at this point I'm fuzzy. Are you wanting the object "Bryan_edds" to be accessable from the server, or are you wanting a duplicate ghosted to the client so the client can access "Bryan_edds_ locally?
#6
08/26/2004 (7:21 pm)
I think you got it! Actually, I want to be able to do both of the things you ask about (albeit with different objects of course).

But let's tackle this one scenerio at a time.

I think you hit it on the head with the three step explanation, but I'll go through exactly what I want to do to keep us on the same page. Say I have instantiated an object called Bryan_Edds on the Server like so -

new NetObject(Bryan_Edds)
{...};

And let's suppose I have code running on the Client that wants access to a ghost of Bryan_Edds. Could the Client use the handle Bryan_Edds to access that ghost like this -

Bryan_Edds.getVelocity();

If not, how else might I do it? And finally, do I have write access to ghosted objects (in other words, the ability to call non-const methods on the ghost)?

That's one thing I'm wanting to know. The next thing I need to know how to do is the reversal of the previous scenario.

Let's assume that I instantiate a NetObject name Gonzo_T on the Client (instead of on the Server like before) like this -

new NetObject(Gonzo_T)
{...};

- and now I want the Server to access it. Does ghosting work both ways, and would I be able to access it the same way as in the reverse scenario, or would I have to do something different? My other questions also apply to this scenario, too :)

I probably should have broken it down into two question in the first place since they might not have the same solution :)
#7
08/27/2004 (12:56 am)
Lets take the ghosting first. To prevent massive abilities to cheat, server and client are handled very differently. When TGE is started up it's in a neutral state of connection. When you start a server, TGE set's internal parameters of the engine to...

GhostFrom = true;
GhostTo = false;


The clients that log into the server will already be set to....

GhostFrom = false;
GhostTo = true;


So you cannot ghost both ways without some engine modding for sure. And I'm not sure how intensive that might be.


Now as for the netObject, I believe you can do it, and here's how I woul do it based on about 2 minutes of thinking about it, lol.


1. Gonzo_T logs into server

2. Server creates a special ghostable object named Gonzo_T where info is tracked...

3. When the new object is ghosted back to the client, the client uses the onAdd or onNewDatablock method to name this nameless object "Gonzo_T" using the current player name variable.

This would allow you to simply ghost information you wanted to have handy without having to do a ton if data updating to keep the clients current.

To access the servers Gonzo_T datablock for something that may not be ghosted like velocity, you'll most likely want to go with a commandToServer setup that is formulated to give you certain access to functions or options.


First you would formulate a new script based datablock for these special objects and give it a unique class name like...

className = "InfoSender";


so that it can be used and accessed in the following fashion....

commandToServer('NeedMyInfo', "velocity");

to call the following function...

function serverCmdNeedMyInfo(%client, %whatInfo)
{
    %obj = getObjectbyName(%client.name);

    %obj.sendInfo(%client, %whatInfo);
}


which converts the calling clients name into a function call to....

function InfoSender::sendInfo(%obj, %client, %whatInfo)
{
     switch$(%whatInfo)
     {
          case "velocity":

                %info = %client.player.getVelocity();

          case "foo":

                %info = %client.foo;
     }

     commandToClient('YourInfo', %whatInfo, %info);
}


Without knowing the exact use for which all this is intended, this is about the safest way I can muster off the top of my head to achieve what results I think you are looking for. However, if you are willing to take some risk, you can gain far more power by utilizing the "eval" function in the following manner continued in the next post....
#8
08/27/2004 (1:31 am)
First you adjust your server command...


commandToServer('NeedMyInfo', "" @ $pref::player::name @ ".getVelocity();");

which will send the command of

Gonzo_T.getVelocity();

to the following function...

function serverCmdNeedMyInfo(%client, %whatInfo)
{
    %info = eval(%whatInfo);

    commandToClient('YourInfo', %info);
}


Obviously that is much easier, and much more powerful, and much easier to hack. Suppose I messed with your scripts and logged into your servers and sent...

commandToServer('NeedMyInfo', "quit();");

When it got to the server, the server would shut down immediately. Obviously not a good idea. So you would want to taylor it to force a player name like so....


commandToServer('NeedMyInfo', ".getVelocity();");

which would then be handled by the server like this....

function serverCmdNeedMyInfo(%client, %whatInfo)
{
    if(isObject(%client.name))
    {
        %info = eval(%client.name @ "" @ %whatInfo);

        commandToClient('YourInfo', %info);
    }
}


Much safer for your server, but still far from foolproof. Suppose you were playing a multiplayer treasure hunting game and the client's name object stores the client's score(I know, it's a stretch) via a variable like...

Gonzo_T.foundTreasurePieces = 37;


Using the above function a client could send....


commandToServer('NeedMyInfo', ".foundTreasurePieces = 1000000000;");


and the server would more than happily apply the new score for me via....

eval(Gonzo_T.foundTreasurePieces = 1000000000;);

And I would be way ahead of the pack in one little eval. My obvious point being that if you intend to share data that would not normally be shared, and you want to make it easy to do, then you are going to have to accept the fact that the easier you make the whole process, the harder it will be to secure it against guys that are hell bent on cheating to win.


Another direction you could approach this from is to adapt the client script for message callbacks to the server and use the "call" function which is a little safer because it would only call functions that existed and therefore only produce data based on proper inputs, but that's a whole new issue again. If your still not sure how to go about this, then we need to examine your ultimate needs and goals to see what direction would be most efficient and SAFE at the same time.
#9
08/27/2004 (11:30 am)
Whoa, Thanks for all the help bro! Man, I owe you one now :)

Considering these security risks you laid out here, it looks as though I'll need to judge more carefully on what I'm giving the client access to.

What I'm working on currently is this issue -

I've want to make something like a Player Profile which can be saved on the Client's drive and altered by the Client. It would have settings that can be loadeed for each individual player profile, such as name, controller settings, video / sound settings, and also restrict access to what saved games that profile can access. I guess the problem I'm thinking about has to do with the fact that the Server is what saves the game, but the Client has the profile the dictates what saved games can be loaded. So, in order to give the Server the info necessary to decide what game saves can be loaded / altered, it needs to access the Client's currently loaded PlayerProfile object.

What I want to do is to expose the PlayerProfile object to the Server from the Client. And this is where I'm having some problems... Would I derive PlayerProfile from NetObject in order to ghost it to the Server, and if so, what would be the best way to do that? I was thinking of just calling a commandToServer function and passing the ProfileObject in that function, but I don't understand what that would accomplish. And by that I mean, if you pass an object's reference to the Server's function from the Client, can the Server actually perform operations on that object even if the object is not local to it? If so, how does it access the object's function which aren't even local to the Server? Does it send those function calls over the network to the Client object's address, or is the object local if it is ghosted from the Client to the Server when derived from NetObject, or something completely different? Or let's even say that the object is NOT ghosted from the Client to the Server, can an object created by the Client be operated on by the Server if the Server receives a reference to it even if the function calls made by the Server are not addressed to the Server's locale?

Phew, this is a lot to figure out. I just haven't much an idea about how ghosting works, what it can do, and how it is achieved in script.
#10
08/27/2004 (12:42 pm)
You may want to check this documentation link on the network code. Ghosting basically replicates the data of an object across the wire. So when the server calls its copy of the object, it operates on the same data and should return the same results. This is different than RPC methods, where you send the call across and get a response back from the client.

If you look at the network code on the c++ side, look at the NetObject code. The methods that send the data back and forth are packUpdate and unpackUpdate.

Hope that helps.
#11
08/27/2004 (9:38 pm)
Thanks, though I already read all that. The problem is that those docs tell you every little thing you never wanted to know about ghosted NetObject, but give you absolutely no clue how to just use the bleeping thing :)

But knowing that a ghosting replicates an object across the wire, (let's say the object was created on the Client and is replicated on the Server), how do I actually _access_ the ghost on the Server? Do I use its name as a handle? Do I have to pass the handle in a commandToServer function? Do I use its SimObject ID? There's got to be some simple way to do it, I'm sure.
#12
08/28/2004 (11:19 am)
Ghosting goes from server to client, not from client to server. If every client could arbitrarily create ghosts on the server, it would open the door for a great number of cheats and DOS attacks.
#13
08/28/2004 (12:17 pm)
Bryan, I haven't forgotten about you or given up on this, just been really busy. I'm still going over it in my head trying to find a good solution.



"There's got to be some simple way to do it, I'm sure."



LOL, you newb, you should know by now that anything you really REALLY want is never going to be easy.

;-)
#14
08/28/2004 (4:30 pm)
@Ben - heh, you're right :) I guess my lack of security-mindedness is showing :)

@Gonzo - This is kind of a contradiction for me with Torque. On some days, I find out new things which makes me stand in awe of Torque's power and ease of use. But on the seldom odd day, things seem to become rather counter-intuitive. :) So that might explain my assumption that there's is a simple approach to what I'm doing :)

@all - Since Client objects cannot be ghosted to the Server (as Ben said), I'm thinking of a couple of different approaches. First, I wonder if its possible to create the PlayerProfile object on the Client, then pass it to the Server in a commandToServer function. To me, that approach might look like it would work, but I'm not sure if that really makes sense since I don't know how the Server would be able to call functions on a non-local object it merely gained a reference to. Unless, of course, passing an object through a function to the Server passes a copy of the entire object in the parameter instead of a reference. That would work, though it would present a couple more issues along with it (which I won't go into because they're irrelevant to this particular problem).

Or I could simply place the PlayerProfile object on the Server, and have some commandToServer functions that the Client would use to makes queries to the Server about the PlayerProfile. This approach would be simple, if inelegantly (IMO) indirect. But what I dislike about this approach is that it just seems so counter intuitive to me to store things like player control and engine graphics settings on the Server. Counter-intuitive, as I said, but perfectly workable.

Anyway, that's the ideas I have now with the limited knowledge I have about Torque's networking. I appreciate you guys helping me, and if you all come up with a more intuitive means of acheiving this goal, I would be grateful to hear your ideas :)
#15
08/28/2004 (7:18 pm)
You could do a hybrid... Have a ClientPlayerProfile and a ServerPlayerProfile. The client one stores all the local settings (controls, render options, name etc) and the Server one stores what savegames they have access to.

When you create one on the client, you assign it a unique ID (eg 1,2,3 etc, no two profiles have the same id). Then you send a message to the server to create a new one with the same id.

When the client logs into the server, it sends its id and the server loads up the appropriate ServerProfile, then sends a list of savegames for that client.

When the client sends a savegame command to the server, the server then stores the gamestate and puts the name of the savefile in the ServerProfile, for access later by the client.

Thats what I would do...
#16
08/30/2004 (1:00 pm)
Indeed.
you wouldn't want anything which controlls access to be stored on the client. store it on the server and access it via client ID.

put yourself in the gamer's shoes and ask youself "i'm a clever guy, how could i go about cheating or being mean here?"
#17
09/04/2004 (12:01 pm)
That's an excellent programming habit, one I try to follow... It can save you VAST amounts of trouble.