I've found a solution to this problem.
Note: this will be a relatively long answer.
One thing that I did implement was the use of udp
for some of my data transfers that are not as critical as others. For example, since I'm sending movement data about 10 times a second, I figured its ok if 1 or 2 of the 10 get lost once in a while.
Now to the actual problem:
So what was happening is the following- since I'm using a p2p
architecture, both clients have a delay when they see the game 'world'. This means that on my device, I see the enemy player at a position that is delayed 100-200 milli seconds. (so he was actually there 100-200 ms in the past).
The problem with this, is that, when I shoot a projectile at the enemy, and I see a collision, if the collision was right on the edge of his sprite (his feet or head), in his screen, he was already past that position - PLUS - my fireball appears 100ms delayed on his screen. This means on his device, he was able to dodge my attack. This can happen a random number of times, but I'd guess its probably below 30% of the time. 70%, both devices see the collision.
The solution
What I came up with is to send a message to the other player when either device sees a collision. And remember, each device has no idea if the other saw a collision or not. So I have no way of knowing whether both devices saw a collision, or only 1, and the other saw that the attack was dodged.
This means I have to send a message to the other player every time theres a collision. Now, because of the way I've architected my game, when a collision is sent, I'm automatically applying the collision event (meaning, damage dealt, projectile effect that took place - like ice bolt freezing the player it collided with) This is problematic, because what if both devices saw the collision. That means both devices are sending each other a message of collision, and applying collisions again.
To get around this, I've added a "spell number" to each spell/attack, and when a collision happens, I save this number with the player that the collision happened with as the "last collision spell number". So if a collision takes place with that spell, the player knows he/she already collided with that specific object, so collision logic doesn't run twice.