swimming hitboxers are uh... nice... and more extrapolation!


(SCDS_reyalP) #41

Attempting to post my understanding of this at a more general level:

On the server, at level.time L a given clients pos.trTime will be the serverTime C of their most recent command. After each client think, and before each snapshot, the clients pos.trBase is the ps->origin generated by Pmove.

This is a contradiction: Is the trBase (and ps->origin and everything else that gets set from it) supposed to be the clients position at time L, or time C ? When interacting with other entities on the server, it is treated at as L, but when sent to the clients, it is treated as C.

In normal operation, the client tries to interpolate entities from snap to nextSnap. The timestamps on these are the L values from when G_RunFrame was run just prior to that snapshot. Each player entity in the snapshot has a trajectory_t pos, with a time C.

When the client goes to position the player for a given frame, it evaluates the trajectories using the snapshot time L. Thus even when interpolating between what were two valid positions on the server, it gets different results. It is treating what the server used as a position at time L as a position at time C and then extrapolating it forward to L. It then interpolates between those two extrapolated positions!

The trDuration is 50, but the difference between L and C is by observation, always more. Since the client will only extrapolate up to 50, that explains your player position being 50ms ahead of where it was on the server at time L. It also explains why a player extrapolates into solid objects, because even when interpolating between to valid snapshots, whose trBase positions are not in solid, evaluated positions will be extrapolated 50ms. Finally, it almost certainly introduces some jerkiness into player movement.

It is not clear why, even on a LAN, is there such a big delta between C and L in ClientEndFrame.

It seems to me that the client should treat the entity positions in the snapshots as being at time L. That is the time the server saw them at that position, and did the authoritative physics on them. Then when the client is properly interpolating, the players will always be truly between two valid positions. Extrapolating the base positions given in snap and nextSnap seems senseless, and wrong. The whole point of having two snapshots is that you are between too valid positions, rather than extrapolating all the time. If we wanted to extrapolate all the time (as we currently are) then we could just use the most recent snap!

When the client is forced to extrapolate (due to missing snaps etc), it should extrapolate forward from the most recent snap.The base position should still be at time L and the extrapolation should be based on how many client frames we are ahead of that.

Thoughts ? Corrections ?


(pissclams) #42

i think i can help with this project, i have experience with email, irc, notepad, and excel.
pls private message me here if you are interested.


(bacon) #43

You’ve gotta be joking. Please tell us you’re joking.


(Sauron|EFG) #44

Lol! Of course he’s joking, Pissclams is a big joke. :slight_smile:


(SCDS_reyalP) #45

Pissclams is known throughout the RTCW/ET community as a paragon of seriousness, a virtual fountain of awe inspiring insights. Why, on my bookshelf, a leather bound copy of Pissclams 2003 qcon beatdown list sits between Platos Dialogs and Newtons Principia , and I dare say, they both look rather petty in comparison. :notworthy:

Now, err, does anyone have any comments on trTime issue ? :banghead: Even someone flaming me because I have it all wrong would be prefered to complete silence.


(SCDS_reyalP) #46

OK, I’ll keep talking to myself.:bump: :banana: :bump:
The code at the root of this (src/cgame/cg_ents.c:CG_InterpolateEntityPosition):


	// this will linearize a sine or parabolic curve, but it is important
	// to not extrapolate player positions if more recent data is available
	BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current, qfalse, cent->currentState.effect2Time );
	BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next, qfalse, cent->currentState.effect2Time );

Note, for players the trTime of *State.pos is well behind *snap->serverTime. However, even if that were not always the case, this logic seems extremely questionable. The pos.trBase represents the known position on the server at snap->serverTime. If our goal is to represent what the server saw at that time, we shouldn’t ever need to mess with the trBase positions. We should just take them as is, and interpolate between them. If we don’t have a valid nextsnap, then we should just extrapolate from the most recent available position, using trDelta and snap->serverTime. In either case, I don’t see why the last command time of the client we are trying to represent should affect how we interpret the position.

As far as I can tell, this code exists all the way back to Q3. Which, if I hadn’t observed the horrible things this causes, would make me assume that I have just competely failed to understand the code.

A few other observations:

  • This also explains why hit detection is so wrong for players who freeze (due to lag) while moving. Again, the model gets extrapolated ~50ms compared to the position on the server. I have verified this by testing.
    edit: well, even without the bogus extrapolation there would still be issues with CIed players, but they would spend much more time in the right place.

  • How does this affect clients view of themselves (rather than their view of other clients) ? Not clear. The extrapolated values look like they will end up in the player entities lerporigin (cg_ents.c:CG_AddPacketEntities), but I don’t see where (if at all) that goes into prediction.

  • Players on movers seems likely to be affected somehow, but I haven’t understood that stuff at all.


(Tavington) #47

Sorry Reyal but that’s really boring to me :zzz:


(bani) #48

movers are borked with antilag btw.

when you fire, the player’s position is backwards reconciled, but the mover’s position isnt.

so quite often youll be aiming directly at a player in a mover (eg tug), to you it is a 100% clear shot with no obstructions. but the server thinks you hit the mover instead.

eventually, antilag should be fixed up to antilag movers as well.


(pissclams) #49

in all seriousness tho, I am pretty good at programming, and just to prove it i will write Bacon an advanced application developed in a very complex programming language which only the fastest Commodore 64’s will run-

10 Print “Hi Bacon, Don’t Listen to Sauron and SCDS_reyalP”;
20 Goto 10
30 End


(Chruker) #50

Now you made me remember all the cool things you could make with the character graphics on the C64… I remember my Titanic animation :slight_smile:


(Chruker) #51

On the topic: Are there any documentation which describes the ‘logic’ behind antilag? Or the best antilag algorithm?


(bacon) #52

Bani explains it quite well. Wel’ll wait for him to come and explain it for you :slight_smile:
While he’s at it, he should also point out the flaws in neil’s antilag code.


(SCDS_reyalP) #53

http://www.planetquake.com/alternatefire/unlagged_faq.html
Also, on the alternatefire site http://www.planetquake.com/alternatefire/ you can download the unlagged2 code for q3, which, along with the code, contains some more extensive documentation. There is a also another thread or 2 on the topic:
http://www.splashdamage.com/index.php?name=pnPHPbb2&file=viewtopic&t=7391&highlight=antilag

The code itself isn’t something you can just drop into et, but is certainly a good starting place. In fact, if you deal the bogus trajectory evaluation issue described in this thread, it should be pretty darn close. Otherwise, you have to deal with the 50ms issue bani described. There were a couple of other things I found dubious too…

I still plan to release my port of the unlagged2 stuff, but there are still a few things I have to sort.


(Hewster) #54

I still plan to release my port of the unlagged2 stuff, but there are still a few things I have to sort.

I Look forward to that reyalP… it seems you are doing a sterling job there :smiley:
I can’t ever seem to find the time to play with the unlagged source ( and get it into
RtCW - WW ), so I for one will be a very happy bunny if you can get this done for the
RtCW & ET community :slight_smile:

Hewster


(TwentySeven) #55

Ayyo, totally forgot about that from baseq3 - you should NOT be using the playerstatetoentstateextrapolate EVER.

Anything relating to g_smoothclients, kill it, you want it at 0

horrible hack that was only implemented to work around the localhost jittery client issue.

shudder


(TwentySeven) #56

Firstly, just to give a quick 101, don’t :rolleyes: if you know this stuff

Firstly we’ll deal with entities and snapshots. The server has an authoritive view of the “world”, which proceeds in fixed 50ms intervals. aka the “server think”.

The time of the world on the server is level.time, only ever increments in 50ms amounts.

So, at the server think, the state of all the entities are packaged up and sent down in what you know as the snapshot, with the level.time stamped on it.

[ Please wait, sending ]

Right, now the client has it. The clients view of the world is determined by interpolating between snapshots. So, providing snapshots arnt late (which is pretty minor/infrequent) cg.time is always somewhere between level.time-50 and level.time.

level.time-50 and level.time, because we’re interpolating between latest-1 and latest, always, meaning that even with 0 ping, your view is always 50ms behind.

Oooo Kay. Lets get onto the clientside movement and “commands”. Every frame the client produces a cmd, and timestamps it with a time between level.time-50 and level.time. Every 1000/cl_maxpackets ms, the client sends the conjealed accumulated commands off to the server. This cmd == what you did at that server.time (which is actually your cg.time on the client, but whatever)

[ Please Wait, sending ]

Ok, so the level.time is always past the cmd.timestamps when the server gets them. The server then sorts by timestamp, and passes each cmd it has into clientthink, making the serverside view of the player advance along to that time. It generally tries to keep players advanced to roughly the same time, to minimize collision issues between players and players. The server objects are completely static during these time adjustments however, at whatever state the prior level.time left them at. Once they’re all done, the server “think” occurs, the time advances another 50ms, and all clients get a client_end_frame(). Then the snapshot gets generated and goes out.

The little flaw there is that theres barely any checking done on the incoming timestamps or consistancy checks, meaning you can be fairly unsmooth to other players by disrupting your upstream. Plenty of threads on this forum about that and antiwarp already, though.

The issue as you guys were asking was, for antilag, you want to record the players positions in clinet_end_frame() - which has the correct position in ent->s.pos.trBase
for where the player is recorded in that particular snapshot. The timestamp is simply level.time.

As for the extrapolation/g_smoothclients. tres bad. Die die die. kill it.


(Korollary) #57

reyalP

I’ve seen a bot (model sorlag, which almost fills up the box) penetrate a wall on pro-dm6 in Q3 using the latest CPMA (localhost).


(SCDS_reyalP) #58

VQ3 almost certainly has the same problems. I’d kinda guess that OSP does too, since it wasn’t fixed in ET. No idea about CPMA. There are of course a lot of other things that can cause models to go into walls. I’m not sure how this will work out on a local game either, since that is allegedly why it was created in the first place.

Hmmm, did the MIB come and take twentyseven away ? I’d really like to see the rest of that post. It matches my conclusions completely so far.

BTW, this explains why neils unlagged code didn’t have the 50ms adjustment that bani saw… because his antiwarp stuff replaces the broken smoothclients stuff.

Of course, adjusting 50ms on the server isn’t realy right, because the client extrapolation isn’t going match what the server actually did most of the time. This may contribute to why madly strafing back and forth is such a good tactic in ET, because at the transitions, you get a nice big warp from extrapolating in one direction to the other. Also, if you turned smoothclients off, antilag would be 50ms wrong in the other direction. Rapid direction changes in general should give you quite a bit of error.


(TwentySeven) #59

As for the whole issue of being late and the VALID reason to use extrapolations of client snapshots, I’ve recently had to investigate this for UrT.
The main reason cg.time would run past the nextsnap.leveltime is if your downstream is fluxy - ie, you have disruptions in the green part of your netgraph.

The trick then isn’t to extrapolate and hope you did the right thing, but to smooth out the incoming snapshots so you’re world-view on a client stays consistant.

this is what cl_timenudge is SUPPOSED to do, but is evil as it tampers with your upstream cmd packets in an odd way as well.

Havn’t fully investigated what needs to change but my guess is, by artificially inflating the timestamps on incoming snapshots by rounding up to the next valid 50ms interval (or whatever) you’d trade 0-50ms worth of extra “ping” for removing 0-50ms worth of flux from your downstream.

The gotcha is you’d have to let antiwarp know you were doing this for a given client, because the lookup time on reconciliation would need to be tampered with as well.

Also has the ugly side issue of players turning it on and off quickly. bitches. I hate players.


(TwentySeven) #60

Anyhow,
In both clientendframe and clientthink real you should have something that looked like this, sans comments :slight_smile: Die g_smoothclients, die!


///}//	if (g_smoothClients.integer) {
//		BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
//	}
//	else {
		BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
//	}

Then regarding antiwarp, its just a matter of

  1. make sure you record level.time as the timestamp during client_end_frame() and ent->s.pos.trbase as the origin.

  2. use the exact cmd.time to do the lookup when a client shoots.

The end :slight_smile: