real time bullets in a MORPG?

Started by
26 comments, last by graveyard filla 19 years, 4 months ago
hi, im working on a 2D action based MORPG. combat is real time, and the game is futuristic - this means when i pull a trigger, i see a bullet come out of the gun and go towards the target. do you think its realistic of me to keep this in game? currently, i don't even track bullets or confirm anything on the server, but obviously that has to change and i have to do that. im just worried that this will be too slow, mainly bandwith wise and possibly even computational wise. more information: -the game is 2D, tile based -my goal is to handle 30 players at once on a dedicated machine running on a cable modem. however, this is my realistic goal. my true goal / what im aiming for is as many clients as possible. -the way projectiles work is this: 1)client pulls the trigger 2)client sends to the server "I shot a bullet standing here, at this time, with this gun" 3)server receives message, bounces it to all clients in his area 4)client receives the message, adds the bullet to the world, sets position / velocity and makes up for lost time using the timestamp the way i will change it to is add these: 3.5) server adds this projectile to his list, sets position / velocity and makes up for lost time 5) server does collision detection each frame, checking for collision and doing appropriate hit/miss and damage done equation 6) server sends message to the client "the bullet hit/missed and did X damage to this client" there are a few problems with this system. 1) it will be hard to synchronize. on my client the bullet might appear to hit the person i shot at. but the server might think that the bullet missed and is still flying in the air. how do i resolve this? obviously the server has the "right" result, but how do i go about telling the client "whoops, sorry, actually that bullet didn't hit that guy and its still flying in the air, probably about to hit a wall or possibly even another person"? how would i know when to send the packet, how would i know on the client to draw blood / particle effect if i cant be guarenteed that it actually did hit there, etc... synchronization will be hard to just make sure everything is right. i use timestamps with everything so in theory everything *should* be synchronized, but, well, you never know... 2) bandwith. to be honest, i think how it is now is very low bandwith. 1 byte ID saying the type of message (MSGID_WEAPON_FIRED), another byte ID saying the gun that shot it (WPNID_PISTOL for ex.), then 2 bytes X position, another 2 bytes Y position, plus 4 byte timestamp... this is 10 bytes total plus raknet overhead (not sure how much this is...). prediction should take care of the rest. however, resolving conflicts could add potential bandwith and like i said im not sure how to resolve those conflicts in the first place.. i should also note, the client *shouldn't* send his X/Y position with that packet. this is 2 reasons - the main being security. the client should never tell the server where he/his bullets are. the second issue is bandwith - i could shave 4 bytes off the packet by not including this, since the server *should* know where i am. the main reason why i do include this in the packet is for synhronization reasons - the server might be off in where i really am, and this helps synchronize things... 3) CPU usage. not sure if this is any issue at all, but the server would have to do bounding box collision on possibly hundreds or thousands of bullets. this is probably a non-issue though. lastly, i really do want this to be implemented. i think having real time bullets will make combat more fun - and definetly will make it easier on me (from a design and even programming standpoint) to make the combat fun. so, what are your thoughts opinions? should i keep it like this, and if so how do i resolve conflicts? thanks for any help.
FTA, my 2D futuristic action MMORPG
Advertisement
We discussed this in #gamedev a bit, and here's a more elaborate answer (so others can find it if they search):

There are really two problems:

1) how can the client see immediate reaction from an action?
2) how can we make the server be authoritative?

We could solve problem 1) by making the client authoritative. Thus, you shoot, your machine rolls a die, and you hit or miss. The result is propagated to the server, whence it bounces to everyone else. The problem with that solution is that it opens you up for cheating -- you really want the server to decide.

You could solve problem 2) by compromising problem 1), having the client send a message when shooting, and waiting for reply from the server before seeing blood or a blinking "Miss" text or whatever. If the round-trip is fast, and the bullets are slow, this might be good enough.

However, a more intriguing solution presents itself in the fact that it's an RPG -- there's a computer algorithm that determines the success/failure of an action (rather than player skill). The core is to have a random number generator determine the outcome, and have the random number generator generate the same result for the same action on the client and on the server.

Assuming that all involved objects in the world have a globally consistent Id (i e, you are always object 213, and the specific monster in question is always object 87123), and you keep a consistent time across machines (probably synced from the server), then you could say that the random number is a hash of the two interacting objects, and the second number which they interact in. You can keep a more accurate synchronized clock if you wanted to resolve more than one action per second. I e, to come up with the random number for a skill roll where you shoot the monster at time 123565, the random number generates hash(213,87123,123565), which you then treat just like you would the output of rand(). The action sent to the server is "I used skill X on monster Y at time Z" so the server (and, in turn, all clients viewing you) can arrive at the same result.

However, this makes it possible to reverse engineer the algorithm, and given object Ids and time, come up with the "right" times to make skill rolls. Run the hash for each second from now and 10 seconds forward, and have a macro time the key press to make it most likely to succeed within that time. To get around this, the server should send an additional "salt" to the player after each skill roll (say 9834 for this instance). When the player wishes to perform a skill, the player sends not only target and time, but the salt value, and the salt value goes into the hash function. I e, rand() for the skill roll is now hash(213,87123,123565,9834). The server immediately issues a new salt value, and will discard any further skill requests using the old salt value. If you need to perform more than one skill roll during the interval of a single server RTT, then you can use a sliding window (ordered queue) of salt values. This is still not perfect, as someone could make their macro also intercept the salt values and still run the calculation, but you've significantly upped the ante of creating a skill timing cheat. You could even make it so that you issue a new salt every 5 seconds, unconditionally (even if no skill has been used), at a slight cost in bandwidth.
enum Bool { True, False, FileNotFound };
Shouldn't be too much of a problem. Replicate the movement on the client with the vector and speed and make the server take the actual load and computing. There are MMORPG's that let the server alone do this, and it works quite well.
SymLinked: if that's how they do it, then how do they make a bullet come out of the gun of the player the instant the player presses the "fire" button? From what I understand that you describe, I'd think that after pressing "fire" I would have to wait for a round-trip to the server to actually see the action.
enum Bool { True, False, FileNotFound };
Cheat!

do a muzzle-flash the instant the player squeezes the trigger, and then when you get a response from the server, put the bullet at the porper location, maybe with a con-trail type effect backtracking to the weapon.
Quote:Original post by lonesock
Cheat!

do a muzzle-flash the instant the player squeezes the trigger, and then when you get a response from the server, put the bullet at the porper location, maybe with a con-trail type effect backtracking to the weapon.


Why not take it a step further and actually create the bullet when the player hits the fire key and then update the bullet’s position (assuming the bullet still exists) upon receiving confirmation? As long as the server handles damage dealt and informs the clients then this should work fine (bullets are just dummies on client side).
Note: that it how I go about it. It may not be the ‘correct’ way.

Hope this helps,
Jackson Allan
hey everyone,

first, let me apologize for taking so long to respond. i had run to a horrible bug that had completely de-railed my development and it took me a while to figure out.

anyway, now im back and can start deciding how to implement this system.

@hplus,

i haven't used hash's before, in fact, to be honest, im not sure even what a hash is. i think it has something to do with crpytography. your system sounds very nice though, im going to have to google this hash thing.

i actually just had an excellent thought about this. think about it - when a bullet hits a player, the server has to do a calculation based on skill level, and then determine if it was a true hit or a miss. if its a hit, he then also has to do damage calculations.

who says this has to happen when the bullet actually hits the object? why not, the instant the bullet is fired, the server does this calculation and sends it to the client? then, IF the bullet does physically hit his target, he will then use this result.

this brings us to the real issue then - making everything synchronized so that it actually does physically hit the target on all machines. this is how it works now, and it seems very accurate, but i fear that it is not, and my real question is how do i go about resolving conflicts and ensuring syncrhonization.

first of all, this is how it currently works-

1)a player selects a target, and pulls the trigger. he sends to the server "im standing here, at (X,Y), i fired at a target at (X,Y), im holding gun G and this all happend at time T". this client then adds this projectile in his world.

2)the server gets this message, stamps an ID onto the packet, and sends it to the other clients. NOTE: he does not track bullets himself!

3)a client receives the message, puts the projectile in his world at the position,and then "jumps" the bullet to make up for lost time based on the time stamp.

now, this is wrong for several reasons. first of all, everything is client authoratative. this means not only could a person hack the clien and probably instantly kill their enemies with the push of a button, but it also throws a great risk of things falling out of synchronization. anyway, heres my new propsed plan (please C&C)

1)a player selects a target, and pulls the trigger. he sends to the server "i fired at client X at time T". thats it. the server should know 3 things

-what type of gun the client is holding
-where the client who fired is standing
-where the cleint who was shot at is standing

this prevents a client from trying to give himself the super-awesome-gun-of-instant-destruction via packet spoofing. it also stops the client from telling the server some imaginary coordinates that he is standing at, and it also stops the client from doing the same with his target. not only this, but it cuts down on bandwith dramatically. instead of having to send 8 bytes worth of positions and 1 byte weapon ID (plus timestamp), i only send 2 ID's and a timestamp.

2)the server receives this message. he adds the bullet to his own simulation, makes up for lost time via the timestamp, puts the senders ID in a packet, then sends the packet out to all the clients (who are in his area...). he also does a hit/miss calculation and includes it into this packet, and also a "damage done" calculation and also sends this result to the client who fired the gun.

3) the client receives this message, and adds the bullet to his simulation.

4)now, all clients AND the server are tracking this bullet - in theory, they should all be in the exact same spot. when the bullet hits on the client side, it checks weather or not it is a true hit or miss, and then either does a particle effect blood spurt or a little "miss" thingy. it also adjusts the health on their local simulation.

5)when the server knows the outcome of the result (e.g. if the bullet physically hit the target), he sends this to the clients just in case - if it was a miss and the client thought it was a hit, the client has to give back the health to the character.

how does this sound? there are several flaws that concern me

- in step 1), what if the client quickly switch's weapons and then fires? would it even matter? im leaning towards no, but im not sure...

- in step 5, is the last part even needed? can i rely on the simulation running syncrhonized? or do i need to send this just in case? this is more of a general networking question, can i really rely on my game being sync'ed, or do i need this confirmation packet? also, my other concern is this - what if the client thought it was a hit, but the server thinks that it missed, and its still flying through the air. then what? hell, what if it hits a different client at that point? this is kind of mind boggling and im not sure how to handle this...

thanks a lot for anymore help!
FTA, my 2D futuristic action MMORPG
Quote:- in step 1), what if the client quickly switch's weapons and then fires? would it even matter? im leaning towards no, but im not sure...


If your packets are guarunteed to arrive in order, then no, this won't be a problem, since your change-weapon packet will arrive before the shot is fired.

Quote:- in step 5, is the last part even needed? can i rely on the simulation running syncrhonized? or do i need to send this just in case?


It really depends on how solid the synchronization of your client/server architecture is. Sending out a few extra bytes "just to be safe" probably won't be anysort of burden on the server.

Quote:also, my other concern is this - what if the client thought it was a hit, but the server thinks that it missed, and its still flying through the air. then what? hell, what if it hits a different client at that point?


This is contradicting the design you just proposed, though. If the server knows that it missed, then tell the clients (in range) that it missed. You shouldn't be deleting the bullet clientside just because the client *thinks* that it hit. As for it hitting a different client, how can that happen if client_hit != client_specified_in_original_packet?

I've been writing my own online action-y game for a while too, perhaps we could talk a bit in realtime (MSN? AIM?) about the design. I have no quarrel with helping you out in avoiding the pitfalls I fell in when I wrote my game the first (of several) time(s). :)
I think the server needs to track at least the hit-or-miss of the bullet. To do this, the client needs to send what specific target it's shooting at. Thus, the data could be something like:

"I'm object I, at Ix,Iy, holding gun G, firing on target O, at position Ox,Oy, at time T."

Server would check the client position just like it checks movement commands -- is this a reasonable movement from the last known position at the last known time, updated to time T? If not, reject packet and send "miss" to everyone.
Then check the target position -- is that a reasonable position for the target at time T, given the last packet sent to the client? If not, reject packet and send "miss" to everyone. This means that people with really high packet loss will miss more -- tough luck!
Now, do the skill roll -- attack skill of I, compared to defense skill of O. Send out the result of the skill roll to everyone, together with the data about who fires and who is fired upon.

The local clients will generate bullet shots from the entity to the entity -- not from the X,Y to the X,Y! However, they will perhaps use the X,Y values to update their notion of where the entities are; at least the position of I (because the client can control I within reason).

This will work fine, as long as bullets travel slower than server ping times.

As for hash functions, they really are just a way to get a "hash code" (arbitrary number) out of some arbitrary input data (string, integer, struct, whatever). This is used to decorrelate input data with something like sorting order or, in this case, pseudo-random numbers. A good hash function for arbitrary data is "PhongHash"; I'm hoping that will come up on Google.

Also, hashing is great for look-up tables that need to take some Id which is not a small integer. However, if you have std::hash_map and std::hash_set available, you don't necessarily need to write those containers yourself -- but unless you need sorting, hash_map is significantly better than std::map (faster, smaller).
enum Bool { True, False, FileNotFound };
How do first person shooters solve this problem? This problem isn't unique to RPGs.

This topic is closed to new replies.

Advertisement