Sign in to follow this  
Vaughands

Tracking users in a virtual world

Recommended Posts

I have a virtual world (MMO environment) in which I require users to login with a username and password into the world. Currently, when a user connects and login I store their socket/connection info in a dictionary; the socket being the key and the account object being the value. However, I later find the need to when for example: "A sends a message to B" "Server reckgnonzies this; and B is identified as Bob in the packet" "Server now needs to know what socket Bob's is" How can I figure this out without using a very slow for loop?

Share this post


Link to post
Share on other sites
Quote:
login with a username and password


This means that there is some store, a database perchance, which has a list of all user/password pairs.

Assign each of these a unique number (ID). If using a database, primary or any auto increment key will do.

Quote:
I store their socket/connection info in a dictionary

Instead, store their ID. Then have the following structures:
- Player, which holds socket, ID and anything else
- ID->Player dictionary

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Quote:
login with a username and password


This means that there is some store, a database perchance, which has a list of all user/password pairs.

Assign each of these a unique number (ID). If using a database, primary or any auto increment key will do.

Quote:
I store their socket/connection info in a dictionary

Instead, store their ID. Then have the following structures:
- Player, which holds socket, ID and anything else
- ID->Player dictionary


Wouldn't the username already function as a unique identifier?
You probably want a sort of connection/player object that stores both the socket and the username.
When a socket receives data it can easily grab the username in the same object.

Also if someone for example, send a tell to "username", you can have a dictionary with every connection/player object using the username as key.
Then lookup the username in the dictionary to fetch the right connection/player object which contains the socket.

Share this post


Link to post
Share on other sites
If the problem is "how do I go from socket (int) to user information" then a std::hash_map is probably the right answer in C++, and a similar container (Dictionary<>, dict(), etc) in other languages. This works for TCP.

For UDP, you will have to map remote IP address/port (as seen in recvfrom()) instead, but it's the same idea.

Trusting a user ID sent by the user on the wire is not a good idea, because the user may lie (hack their client).

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
If the problem is "how do I go from socket (int) to user information" then a std::hash_map is probably the right answer in C++, and a similar container (Dictionary<>, dict(), etc) in other languages.

Trusting a user ID sent by the user on the wire is not a good idea, because the user may lie (hack their client).

You don't have to trust the user to get the ID from him, you just need to verify it. I'd use a simple std::vector and the user sent ID for indexing, then for validation I'd compare the stored IP address/port with the sender's.

Share this post


Link to post
Share on other sites
Quote:
You don't have to trust the user to get the ID from him, you just need to verify it.


Right, but what have you won? Using a vector instead of a hash_map? And, at that point, you have to write more complex code, because you have to also test that the ID is not out of range for the vector. Plus you have to manage the vector integer name space. Neither of which is hard, but all of which has more possibilities for bugs than just std::hash_map::insert()/erase(). (Or, if your stdlib is older, std::map)

Share this post


Link to post
Share on other sites
The issue indeed is connection to user info. I'm pairing the end point (UDP) to the dictionary (C#) or hash table in many other languages. The issue here is, if my packet information refers to another user. How would I reference this user easily? Such as sending a 'private' message?

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
Quote:
You don't have to trust the user to get the ID from him, you just need to verify it.


Right, but what have you won? Using a vector instead of a hash_map?

I just like the lightweight, simple and fast solutoins. Why hash_map if you get away with a simple vector?

Quote:

And, at that point, you have to write more complex code, because you have to also test that the ID is not out of range for the vector. Plus you have to manage the vector integer name space.

you just deriver from std::vector and overload the operator[] where you index by size(), not that many bugs you could cause. sure, those are some more lines of code compared to using hash_map, but the complexity of the overall code is less.
it's like a hashmap without the need of hash and collision-resolving (just the bounds checking).

Quote:
Neither of which is hard, but all of which has more possibilities for bugs than just std::hash_map::insert()/erase(). (Or, if your stdlib is older, std::map)

sure, you could use those, you could always use those, but I guess nobody does if he gets away with less complex solutions (although the complexity is hidden under the interface of the stl).

Share this post


Link to post
Share on other sites
I guess I don't see the complexity of the map based approach?

Vector:

0) receive user packet with IP
1) read id from the user packet
2) check whether ID is in range for vector, if not -> bad id
3) read user information from vector
4) compare IP address in user information with IP from user packet, if different -> bad id

Hash map:

0) receive user packet with IP
1) retrieve user information from hash map using IP, if not there -> bad id

Of course, this is such a small bit of data that it doesn't really matter, and won't show up in a profiling tool ever. Use whatever you're most comfortable with :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by Vaughands
I'm using a hash map.. it seems fast & efficient.. and in profiling I've found no faster method. My question above still remains.


Talking about sockets here is probably just a bit too low on abstraction scale. Usually, each connected user would have a queue of messages to be sent to them.

For single machine, shared memory, the solution is OO-like.
class Player {
Player(World w) {
w.nameLookup.put(name, this);
w.idLookup.put(id, this);
}
void onDisconnect() {
w.nameLookup.remove(name);
w.idLookup.remove(id);
}
void onChat(ChatPacket p) {
String sender = p.getSender();
String target = p.getTarget();
String message = p.getMessage();
w.sendChat(sender, target, message);
}
String name;
int id;
...
List<Message> outgoing;
};

class World {
Map<String, Player> nameLookup;
Map<ID, Player> idLookup;
...

void sendChat(String sender, String target, String message) {
Player p = nameLookup.get(target);
if (p != null) {
p.outgoing.push(new ChatMessage(sender, message));
}
}
};



For distributed systems, the approach is likely similar, but the name->player lookup might not be that trivial (needs database, cache server, name server, DB, ....).

For message-based distributed systems you might do one better. Assign each player a set of channels. So player John would have a global chat queue: /world/John/chat. When someone wants to message them, just sent message to /%zone_name/%username/chat. The messaging system will throw an error if the channel doesn't exist.

For SQL-based system, just do an INSERT (sender, message) INTO PMs. When such messages are delivered, delete them, or similar.

For key/value stores, just append messages to the list/document or whatever schema they use in that user's store.

Share this post


Link to post
Share on other sites
Quote:
How would I reference this user easily


If the target user is not connected, you will need a persistent store. Then when the user connects, you check for queued messages, and send them.

When the target user is connected, you can still use the same mechanism, but you need to "poke" the user's client so it knows to go fetch new messages (or just have it receive the messages right away). To do this, you need a hash table from "user id (or name)" to "connection." That hash table can easily be built and maintained when the user logs in and out.

In fact, if you don't want to allow a user to be logged in from two placed at once, you have to build that table anyway.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this